<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<meta name="theme-color" content="#222" media="(prefers-color-scheme: light)">
<meta name="theme-color" content="#222" media="(prefers-color-scheme: dark)"><meta name="generator" content="Hexo 5.2.0">


  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">



<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.2/css/all.min.css" integrity="sha256-xejo6yLi6vGtAjcMIsY8BHdKsLg7QynVlFMzdQgUuy8=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.1.1/animate.min.css" integrity="sha256-PR7ttpcvz8qrF57fur/yAx1qXMFJeJFiA6pSzWi0OIE=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fancybox/3.5.7/jquery.fancybox.min.css" integrity="sha256-Vzbj7sDDS/woiFS3uNKo8eIuni59rjyNGtXfstRzStA=" crossorigin="anonymous">

<script class="next-config" data-name="main" type="application/json">{"hostname":"gz1234.gitee.io","root":"/","images":"/images","scheme":"Gemini","darkmode":true,"version":"8.12.3","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12},"copycode":{"enable":true,"style":"flat"},"bookmark":{"enable":true,"color":"#222","save":"auto"},"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"stickytabs":false,"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"fadeInDown","post_body":"fadeInDown","coll_header":"fadeInLeft","sidebar":"fadeInUp"}},"prism":false,"i18n":{"placeholder":"搜索...","empty":"没有找到任何搜索结果：${query}","hits_time":"找到 ${hits} 个搜索结果（用时 ${time} 毫秒）","hits":"找到 ${hits} 个搜索结果"},"path":"/search.xml","localsearch":{"enable":true,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false}}</script><script src="/js/config.js"></script>

    <meta property="og:type" content="website">
<meta property="og:title" content="郭泽">
<meta property="og:url" content="https://gz1234.gitee.io/page/4/index.html">
<meta property="og:site_name" content="郭泽">
<meta property="og:locale" content="zh_CN">
<meta property="article:author" content="郭泽">
<meta name="twitter:card" content="summary">


<link rel="canonical" href="https://gz1234.gitee.io/page/4/">



<script class="next-config" data-name="page" type="application/json">{"sidebar":"","isHome":true,"isPost":false,"lang":"zh-CN","comments":"","permalink":"","path":"page/4/index.html","title":""}</script>

<script class="next-config" data-name="calendar" type="application/json">""</script>
<title>郭泽</title>
  





  <noscript>
    <link rel="stylesheet" href="/css/noscript.css">
  </noscript>
</head>

<body itemscope itemtype="http://schema.org/WebPage" class="use-motion">
  <div class="headband"></div>

  <main class="main">
    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏" role="button">
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <i class="logo-line"></i>
      <h1 class="site-title">郭泽</h1>
      <i class="logo-line"></i>
    </a>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
        <i class="fa fa-search fa-fw fa-lg"></i>
    </div>
  </div>
</div>



<nav class="site-nav">
  <ul class="main-menu menu"><li class="menu-item menu-item-home"><a href="/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a></li><li class="menu-item menu-item-about"><a href="/about/" rel="section"><i class="fa fa-user fa-fw"></i>关于</a></li><li class="menu-item menu-item-tags"><a href="/tags/" rel="section"><i class="fa fa-tags fa-fw"></i>标签</a></li><li class="menu-item menu-item-categories"><a href="/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类</a></li><li class="menu-item menu-item-archives"><a href="/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档</a></li>
      <li class="menu-item menu-item-search">
        <a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i>搜索
        </a>
      </li>
  </ul>
</nav>



  <div class="search-pop-overlay">
    <div class="popup search-popup"><div class="search-header">
  <span class="search-icon">
    <i class="fa fa-search"></i>
  </span>
  <div class="search-input-container">
    <input autocomplete="off" autocapitalize="off" maxlength="80"
           placeholder="搜索..." spellcheck="false"
           type="search" class="search-input">
  </div>
  <span class="popup-btn-close" role="button">
    <i class="fa fa-times-circle"></i>
  </span>
</div>
<div class="search-result-container no-result">
  <div class="search-result-icon">
    <i class="fa fa-spinner fa-pulse fa-5x"></i>
  </div>
</div>

    </div>
  </div>

</div>
        
  
  <div class="toggle sidebar-toggle" role="button">
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
  </div>

  <aside class="sidebar">

    <div class="sidebar-inner sidebar-overview-active">
      <ul class="sidebar-nav">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <div class="sidebar-panel-container">
        <!--noindex-->
        <div class="post-toc-wrap sidebar-panel">
        </div>
        <!--/noindex-->

        <div class="site-overview-wrap sidebar-panel">
          <div class="site-author site-overview-item animated" itemprop="author" itemscope itemtype="http://schema.org/Person">
  <p class="site-author-name" itemprop="name">郭泽</p>
  <div class="site-description" itemprop="description"></div>
</div>
<div class="site-state-wrap site-overview-item animated">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
        <a href="/archives/">
          <span class="site-state-item-count">54</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
          <a href="/categories/">
        <span class="site-state-item-count">21</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
          <a href="/tags/">
        <span class="site-state-item-count">51</span>
        <span class="site-state-item-name">标签</span></a>
      </div>
  </nav>
</div>



        </div>
      </div>
    </div>
  </aside>
  <div class="sidebar-dimmer"></div>


    </header>

    
  <div class="back-to-top" role="button" aria-label="返回顶部">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>
  <a role="button" class="book-mark-link book-mark-link-fixed"></a>

<noscript>
  <div class="noscript-warning">Theme NexT works best with JavaScript enabled</div>
</noscript>


    <div class="main-inner index posts-expand">

    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="https://gz1234.gitee.io/2020/10/29/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E9%A2%98%E5%9C%A8%E7%BA%BF/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="郭泽">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="郭泽">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | 郭泽">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2020/10/29/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E9%A2%98%E5%9C%A8%E7%BA%BF/" class="post-title-link" itemprop="url">面试题集</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2020-10-29 11:09:28" itemprop="dateCreated datePublished" datetime="2020-10-29T11:09:28+08:00">2020-10-29</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2023-01-31 14:03:44" itemprop="dateModified" datetime="2023-01-31T14:03:44+08:00">2023-01-31</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E9%A2%98/" itemprop="url" rel="index"><span itemprop="name">前端面试题</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h2 id="面试题集"><a href="#面试题集" class="headerlink" title="面试题集"></a>面试题集</h2><ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s?__biz=MzAxODE4MTEzMA==&mid=2650081252&idx=1&sn=1fedc422a3806fa1f9c3faf31bb2a20b&chksm=83db9a81b4ac1397132de99ebdbdbdad57dcc6785d0b8fe1a5ee2b57dbb960b0fbf65015c3ca&scene=126&sessionid=1603760808&key=54ce6b15dc70fa94e4cee849718a95dcb45463880bfbf73a52f6e49f4e4a65fb8adec9e1c54df8bf81bfa1d78626a8537229cc36083224e425c795f892103475ca5f06542d47eec5dabc5d55c77dc7f9fabc4524bbc83cf94060d9236d1061a0fa026db04b47ae38fdfd65662df5549a11d6cd60ff371f5492081a022254d0e7&ascene=1&uin=MjQ4OTg5MDk4MQ==&devicetype=Windows+10+x64&version=6300002f&lang=zh_CN&exportkey=AXrZ8Ft8M/kmfXMdRQOHyYs=&pass_ticket=Kkp6C7aNRW+SS3CyH29rTpuzIryrfuzR2BkuJOMPRmZ73lUqRYKqbJR1nz5SlRhp&wx_header=0">面试题 1</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s?__biz=MzUyNDYxNDAyMg==&mid=2247486750&idx=1&sn=d7e13a8393b83ac330d9b48690428c0e&chksm=fa2bedf7cd5c64e19fcafbe4dab742b65cfe168ad567f3f799b5fc229a35710eb4164084897c&scene=126&sessionid=1602725812&key=6664ac14267ba66883c13581e1d9e62b3ffc7ddfc44d1984c762bde82d19131986d5d9af50595ab1d798e16e45eddd68ded75929bfc6217a87ec0dcacb393b0aa10b53bcd066f65c7865905a425d129f9f1f110464e3a8faa5601a1b7a192f46240134dd033c0bacd43e93b0b51701140f106a0a52acfaabf76e8fee9f2cae06&ascene=1&uin=MjQ4OTg5MDk4MQ==&devicetype=Windows+10+x64&version=6300002f&lang=zh_CN&exportkey=Adc0WAca8bFpyYT3RtaxAjo=&pass_ticket=gNZw604QfgMyZ5MfqQB17Zb9G0KO/y/Gpe3+UhEBieJBkyQwt1xU8LnZyQLLT598&wx_header=0">面试题 2</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s?__biz=MzI2NTk2NzUxNg==&mid=2247488674&idx=1&sn=3f5c6af2c52365525aa84ff92b9f865b&chksm=ea941651dde39f4790e96e2d8f2530fa23257afb50de8d40d6d318507b873f9d870c0f507863&mpshare=1&scene=1&srcid=1026yMwvhU6WsEBstdZUyIgl&sharer_sharetime=1603682665969&sharer_shareid=1b2206d548f7c54418de346a0102e46f&key=041bb01ba83758f9c012f304255f853e521afbe7bbf65555a0e068f76f2c433eea39d0413b426b59a870039c71945328b288292bbbbac9811706f2f09f6716c482684831e94eab0b6935f37a6a5c8892d4ca9ecd897e139bf608b85a18e8ee5339e931c56cc60e39443738eeb63253718488c0322710c61a17510cbfa97910cb&ascene=1&uin=MjQ4OTg5MDk4MQ==&devicetype=Windows+10+x64&version=6300002f&lang=zh_CN&exportkey=AQ+cZHmGMZ8MBH/qQ1l2YVc=&pass_ticket=L+jndQVDhQl1X8R7c+wxUxrwQN/fivdCt7LVG0oUoik5qA1Gx2ZTiVGm+4shiHQn&wx_header=0">源码面试题</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://github.com/markyun/My-blog/tree/master/Front-end-Developer-Questions">前端开发面试题</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://github.com/h5bp/Front-end-Developer-Interview-Questions/">WEB 前端面试宝典</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://github.com/shfshanyue/blog/blob/master/post/juejin-interview.md">掘金前端面试题合集</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://yuchengkai.cn/">前端面试图谱</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://github.com/biaochenxuying/blog/issues/47">前端面试开源项目汇总 | Github 上 100K+ Star 的前端面试开源项目汇总</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://blog.poetries.top/FE-Interview-Questions/">前端面试常考问题整理 | 按模块和知识点分类</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://juejin.cn/column/6964717704712290317">2021 前端面试题集</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://h5bp.org/Front-end-Developer-Interview-Questions/">Front-end Developer Interview Questions</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://github.com/InterviewMap/CS-Interview-Knowledge-Map">CS-Interview-knowledge-Map</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://github.com/shfshanyue/Daily-Question">Daily-Question</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://github.com/Advanced-Frontend/Daily-Interview-Question">Daily-Interview-Question</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://lucifer.ren/fe-interview">fe-interview 大前端面试宝典</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://github.com/biaochenxuying/blog/blob/master/interview/fe-interview.md">前端硬核面试专题</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problemset/all">LeetCode 算法试题学习</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://www.nowcoder.com/">LeetCode 算法试题学习</a></p>
</li>
</ul>
<p>2021面试题<br><a target="_blank" rel="noopener" href="http://www.cnblogs.com/penghuwan/p/7194133.html">http://www.cnblogs.com/penghuwan/p/7194133.html</a><br><a target="_blank" rel="noopener" href="https://www.zhihu.com/question/60577972">https://www.zhihu.com/question/60577972</a><br><a target="_blank" rel="noopener" href="https://zhuanlan.zhihu.com/p/35493203">https://zhuanlan.zhihu.com/p/35493203</a><br><a target="_blank" rel="noopener" href="https://juejin.im/post/59ffb4b66fb9a04512385402">https://juejin.im/post/59ffb4b66fb9a04512385402</a><br><a target="_blank" rel="noopener" href="https://www.cnblogs.com/fengxiongZz/p/8191503.html">https://www.cnblogs.com/fengxiongZz/p/8191503.html</a><br><a target="_blank" rel="noopener" href="https://yuchengkai.cn/docs/zh/frontend/">https://yuchengkai.cn/docs/zh/frontend/</a><br><a target="_blank" rel="noopener" href="https://leohxj.gitbooks.io/front-end-database/html-and-css-basic/doctype-and-brower-render.html">https://leohxj.gitbooks.io/front-end-database/html-and-css-basic/doctype-and-brower-render.html</a><br><a target="_blank" rel="noopener" href="https://github.com/markyun/My-blog/tree/master/Front-end-Developer-Questions/Questions-and-Answers">https://github.com/markyun/My-blog/tree/master/Front-end-Developer-Questions/Questions-and-Answers</a><br><a target="_blank" rel="noopener" href="https://github.com/qiu-deqing/FE-interview">https://github.com/qiu-deqing/FE-interview</a><br><a target="_blank" rel="noopener" href="https://github.com/FEGuideTeam/FEGuide/blob/master/%E6%A1%86%E6%9E%B6/vue.md">https://github.com/FEGuideTeam/FEGuide/blob/master/%E6%A1%86%E6%9E%B6/vue.md</a><br><a target="_blank" rel="noopener" href="https://juejin.im/post/5bc92e9ce51d450e8e777136">https://juejin.im/post/5bc92e9ce51d450e8e777136</a><br><a target="_blank" rel="noopener" href="https://juejin.im/post/5c0e68ec6fb9a04a01642925">https://juejin.im/post/5c0e68ec6fb9a04a01642925</a></p>
<p>2022面试题<br><a target="_blank" rel="noopener" href="https://barryyeee.github.io/InterviewGuide/">https://barryyeee.github.io/InterviewGuide/</a><br><a target="_blank" rel="noopener" href="https://github.com/CyC2018/CS-Notes">https://github.com/CyC2018/CS-Notes</a><br><a target="_blank" rel="noopener" href="https://github.com/huihut/interview">https://github.com/huihut/interview</a><br><a target="_blank" rel="noopener" href="https://github.com/azl397985856/leetcode">https://github.com/azl397985856/leetcode</a><br><a target="_blank" rel="noopener" href="https://github.com/azl397985856/fe-interview">https://github.com/azl397985856/fe-interview</a><br><a target="_blank" rel="noopener" href="https://github.com/jiajunhua/imhuay-Algorithm_Interview_Notes-Chinese">https://github.com/jiajunhua/imhuay-Algorithm_Interview_Notes-Chinese</a><br><a target="_blank" rel="noopener" href="https://github.com/WenDesi/lihang_book_algorithm">https://github.com/WenDesi/lihang_book_algorithm</a><br><a target="_blank" rel="noopener" href="https://github.com/julycoding/The-Art-Of-Programming-By-July">https://github.com/julycoding/The-Art-Of-Programming-By-July</a><br><a target="_blank" rel="noopener" href="https://github.com/helloqingfeng/Awsome-Front-End-learning-resource">https://github.com/helloqingfeng/Awsome-Front-End-learning-resource</a><br><a target="_blank" rel="noopener" href="https://github.com/poetries/FE-Interview-Questions">https://github.com/poetries/FE-Interview-Questions</a><br><a target="_blank" rel="noopener" href="https://space.bilibili.com/9099840/video">https://space.bilibili.com/9099840/video</a><br><a target="_blank" rel="noopener" href="https://jishuin.proginn.com/p/763bfbd23be3">https://jishuin.proginn.com/p/763bfbd23be3</a><br><a target="_blank" rel="noopener" href="https://github.com/phodal/fe">https://github.com/phodal/fe</a><br><a target="_blank" rel="noopener" href="https://github.com/lefex/FE">https://github.com/lefex/FE</a><br><a target="_blank" rel="noopener" href="https://github.com/haizlin/fe-interview">https://github.com/haizlin/fe-interview</a><br><a target="_blank" rel="noopener" href="https://github.com/qiu-deqing/FE-interview">https://github.com/qiu-deqing/FE-interview</a><br><a target="_blank" rel="noopener" href="https://github.com/lgwebdream/FE-Interview">https://github.com/lgwebdream/FE-Interview</a></p>
<p>2022年算法知识集合<br><a target="_blank" rel="noopener" href="https://leetcode.cn/">https://leetcode.cn/</a><br>Leetcode<br><a target="_blank" rel="noopener" href="https://www.gitbook.com/book/siddontang/leetcode-solution/details">https://www.gitbook.com/book/siddontang/leetcode-solution/details</a><br>LeetCode题解<br><a target="_blank" rel="noopener" href="https://zhuanlan.zhihu.com/p/38005681">https://zhuanlan.zhihu.com/p/38005681</a><br>如何高效使用 LeetCode<br><a target="_blank" rel="noopener" href="https://www.zhihu.com/question/280279208/">https://www.zhihu.com/question/280279208/</a><br>大家都是如何刷 LeetCode 的？<br><a target="_blank" rel="noopener" href="https://github.com/MisterBooo/LeetCodeAnimation">https://github.com/MisterBooo/LeetCodeAnimation</a><br>用动画的形式呈现解LeetCode题目的思路<br><a target="_blank" rel="noopener" href="https://programmercarl.com/">https://programmercarl.com/</a><br>代码随想录<br><a target="_blank" rel="noopener" href="https://labuladong.github.io/algo/">https://labuladong.github.io/algo/</a><br>LABULADONG 的算法网站</p>
 <div class="pdf-container" data-target="./前端面试题整合.pdf" data-height="500px"></div>

<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kity@2.0.4/dist/kity.min.js"></script><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kityminder-core@1.4.50/dist/kityminder.core.min.js"></script><script defer="true" type="text/javascript" src="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.js"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.css">
      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="https://gz1234.gitee.io/2020/10/29/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E9%A2%98/css%E9%9D%A2%E8%AF%95%E9%A2%98/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="郭泽">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="郭泽">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | 郭泽">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2020/10/29/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E9%A2%98/css%E9%9D%A2%E8%AF%95%E9%A2%98/" class="post-title-link" itemprop="url">css面试题</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2020-10-29 11:09:28" itemprop="dateCreated datePublished" datetime="2020-10-29T11:09:28+08:00">2020-10-29</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2023-02-28 10:03:22" itemprop="dateModified" datetime="2023-02-28T10:03:22+08:00">2023-02-28</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E9%A2%98/" itemprop="url" rel="index"><span itemprop="name">前端面试题</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h2 id="在CSS中，有继承性的属性主几种"><a href="#在CSS中，有继承性的属性主几种" class="headerlink" title="在CSS中，有继承性的属性主几种"></a>在CSS中，有继承性的属性主几种</h2><ul>
<li>字体系列属性<ul>
<li>font-family：字体系列</li>
<li>font-weight：字体的粗细</li>
<li>font-size：字体的大小</li>
<li>font-style：字体的风格</li>
</ul>
</li>
<li>文本系列属性<ul>
<li>text-indent：文本缩进</li>
<li>text-align：文本水平对齐</li>
<li>line-height：行高</li>
<li>word-spacing：单词之间的间距</li>
<li>letter-spacing：中文或者字母之间的间距</li>
<li>text-transform：控制文本大小写（就是uppercase、lowercase、capitalize这三个）</li>
<li>color：文本颜色</li>
</ul>
</li>
<li>元素可见性<ul>
<li>visibility：控制元素显示隐藏</li>
</ul>
</li>
<li>列表布局属性<ul>
<li>list-style：列表风格，包括list-style-type、list-style-image等</li>
</ul>
</li>
<li>光标属性</li>
<li>cursor：光标显示为何种形态</li>
</ul>
<h2 id="你不知道的-CSS-之包含块"><a href="#你不知道的-CSS-之包含块" class="headerlink" title="你不知道的 CSS 之包含块"></a>你不知道的 CSS 之包含块</h2><p>包含块英语全称为<strong>containing block</strong>，书写 CSS 时，大多数情况下你是感受不到它的存在;但这玩意儿是确确实实存在的，在 CSS 规范中也是明确书写了的：*<a target="_blank" rel="noopener" href="https://drafts.csswg.org/css2/#containing-block-details">https://drafts.csswg.org/css2/#containing-block-details</a>*<br><strong>就是元素的尺寸和位置，会受它的包含块所影响。对于一些属性，例如 width, height, padding, margin，绝对定位元素的偏移值（比如 position 被设置为 absolute 或 fixed），当我们对其赋予百分比值时，这些值的计算值，就是通过元素的包含块计算得来。</strong></p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;container&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;item&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br></pre></td></tr></table></figure>

<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.container</span>&#123;</span><br><span class="line">  <span class="attribute">width</span>: <span class="number">500px</span>;</span><br><span class="line">  <span class="attribute">height</span>: <span class="number">300px</span>;</span><br><span class="line">  <span class="attribute">background-color</span>: skyblue;</span><br><span class="line">&#125;</span><br><span class="line"><span class="selector-class">.item</span>&#123;</span><br><span class="line">  <span class="attribute">width</span>: <span class="number">50%</span>;</span><br><span class="line">  <span class="attribute">height</span>: <span class="number">50%</span>;</span><br><span class="line">  <span class="attribute">background-color</span>: red;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>请仔细阅读上面的代码，然后你认为 div.item 这个盒子的宽高是多少？</p>
<img src="https://xiejie-typora.oss-cn-chengdu.aliyuncs.com/2022-08-14-143451.png" alt="image-20220814223451349" style="zoom: 33%;">

<p>相信你能够很自信的回答这个简单的问题，div.item 盒子的 width 为 250px，height 为 150px。</p>
<p>这个答案确实是没有问题的，但是如果我追问你是怎么得到这个答案的，我猜不了解包含块的你大概率会说，因为它的父元素 div.container 的 width 为 500px，50% 就是 250px，height 为 300px，因此 50% 就是 150px。</p>
<p>这个答案实际上是不准确的。正确的答案应该是，<strong>div.item 的宽高是根据它的包含块来计算的</strong>，而这里包含块的大小，正是这个元素最近的祖先块元素的内容区。</p>
<p>因此正如我前面所说，<strong>很多时候你都感受不到包含块的存在。</strong></p>
<p>包含块分为两种，一种是根元素（HTML 元素）所在的包含块，被称之为初始包含块（<strong>initial containing block</strong>）。对于浏览器而言，初始包含块的的大小等于视口 viewport 的大小，基点在画布的原点（视口左上角）。它是作为元素绝对定位和固定定位的参照物。</p>
<p>另外一种是对于非根元素，对于非根元素的包含块判定就有几种不同的情况了。大致可以分为如下几种：</p>
<ul>
<li>如果元素的 positiion 是 relative 或 static ，那么包含块由离它最近的块容器（block container）的内容区域（content area）的边缘建立。</li>
<li>如果 position 属性是 fixed，那么包含块由视口建立。</li>
<li>如果元素使用了 absolute 定位，则包含块由它的最近的 position 的值不是 static （也就是值为fixed、absolute、relative 或 sticky）的祖先元素的内边距区的边缘组成。</li>
</ul>
<p>前面两条实际上都还比较好理解，第三条往往是初学者容易比较忽视的，我们来看一个示例：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;container&quot;</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;item&quot;</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;item2&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br></pre></td></tr></table></figure>

<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.container</span> &#123;</span><br><span class="line">  <span class="attribute">width</span>: <span class="number">500px</span>;</span><br><span class="line">  <span class="attribute">height</span>: <span class="number">300px</span>;</span><br><span class="line">  <span class="attribute">background-color</span>: skyblue;</span><br><span class="line">  <span class="attribute">position</span>: relative;</span><br><span class="line">&#125;</span><br><span class="line"><span class="selector-class">.item</span> &#123;</span><br><span class="line">  <span class="attribute">width</span>: <span class="number">300px</span>;</span><br><span class="line">  <span class="attribute">height</span>: <span class="number">150px</span>;</span><br><span class="line">  <span class="attribute">border</span>: <span class="number">5px</span> solid;</span><br><span class="line">  <span class="attribute">margin-left</span>: <span class="number">100px</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="selector-class">.item2</span> &#123;</span><br><span class="line">  <span class="attribute">width</span>: <span class="number">100px</span>;</span><br><span class="line">  <span class="attribute">height</span>: <span class="number">100px</span>;</span><br><span class="line">  <span class="attribute">background-color</span>: red;</span><br><span class="line">  <span class="attribute">position</span>: absolute;</span><br><span class="line">  <span class="attribute">left</span>: <span class="number">10px</span>;</span><br><span class="line">  <span class="attribute">top</span>: <span class="number">10px</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>首先阅读上面的代码，然后你能在脑海里面想出其大致的样子么？或者用笔和纸画一下也行。</p>
<p>公布正确答案：</p>
<img src="https://xiejie-typora.oss-cn-chengdu.aliyuncs.com/2022-08-14-153548.png" alt="image-20220814233548188" style="zoom: 33%;">

<p>怎么样？有没有和你所想象的对上？</p>
<p>其实原因也非常简单，根据上面的第三条规则，对于 div.item2 来讲，它的包含块应该是 div.container，而非 div.item。</p>
<p>如果你能把上面非根元素的包含块判定规则掌握，那么关于包含块的知识你就已经掌握 80% 了。</p>
<p>实际上对于非根元素来讲，包含块还有一种可能，<code>那就是如果 position 属性是 absolute 或 fixed，包含块也可能是由满足以下条件的最近父级元素的内边距区的边缘组成的：</code></p>
<ul>
<li>transform 或 perspective 的值不是 none</li>
<li>will-change 的值是 transform 或 perspective</li>
<li>filter 的值不是 none 或 will-change 的值是 filter(只在 Firefox 下生效).</li>
<li>contain 的值是 paint (例如: contain: paint;)</li>
</ul>
<p>我们还是来看一个示例：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;container&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;item&quot;</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;item2&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br></pre></td></tr></table></figure>

<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.container</span> &#123;</span><br><span class="line">  <span class="attribute">width</span>: <span class="number">500px</span>;</span><br><span class="line">  <span class="attribute">height</span>: <span class="number">300px</span>;</span><br><span class="line">  <span class="attribute">background-color</span>: skyblue;</span><br><span class="line">  <span class="attribute">position</span>: relative;</span><br><span class="line">&#125;</span><br><span class="line"><span class="selector-class">.item</span> &#123;</span><br><span class="line">  <span class="attribute">width</span>: <span class="number">300px</span>;</span><br><span class="line">  <span class="attribute">height</span>: <span class="number">150px</span>;</span><br><span class="line">  <span class="attribute">border</span>: <span class="number">5px</span> solid;</span><br><span class="line">  <span class="attribute">margin-left</span>: <span class="number">100px</span>;</span><br><span class="line">  <span class="attribute">transform</span>: <span class="built_in">rotate</span>(<span class="number">0deg</span>); <span class="comment">/* 新增代码 */</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="selector-class">.item2</span> &#123;</span><br><span class="line">  <span class="attribute">width</span>: <span class="number">100px</span>;</span><br><span class="line">  <span class="attribute">height</span>: <span class="number">100px</span>;</span><br><span class="line">  <span class="attribute">background-color</span>: red;</span><br><span class="line">  <span class="attribute">position</span>: absolute;</span><br><span class="line">  <span class="attribute">left</span>: <span class="number">10px</span>;</span><br><span class="line">  <span class="attribute">top</span>: <span class="number">10px</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>我们对于上面的代码只新增了一条声明，那就是 transform: rotate(0deg)，此时的渲染效果却发生了改变，如下图所示：</p>
<img src="https://xiejie-typora.oss-cn-chengdu.aliyuncs.com/2022-08-14-154347.png" alt="image-20220814234347149" style="zoom:33%;">

<p>可以看到，此时对于 div.item2 来讲，包含块就变成了 div.item。</p>
<p>关于包含块的知识，在 MDN 上除了解说了什么是包含块以外，也举出了很多简单易懂的示例。</p>
<p>具体你可以移步到：*<a target="_blank" rel="noopener" href="https://developer.mozilla.org/zh-CN/docs/Web/CSS/Containing_block">https://developer.mozilla.org/zh-CN/docs/Web/CSS/Containing_block</a>*</p>
<h2 id="CSS属性计算过程"><a href="#CSS属性计算过程" class="headerlink" title="CSS属性计算过程"></a>CSS属性计算过程</h2><p>假设在 HTML 中有这么一段代码：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">h1</span>&gt;</span>这是一个h1标题<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>上面的代码也非常简单，就是在 body 中有一个 h1 标题而已，该 h1 标题呈现出来的外观是如下：</p>
<img src="https://xiejie-typora.oss-cn-chengdu.aliyuncs.com/2022-08-13-060724.png" alt="image-20220813140724136" style="zoom:50%;">

<p>目前我们没有设置该 h1 的任何样式，但是却能看到该 h1 有一定的默认样式，例如有默认的字体大小、默认的颜色。</p>
<p>那么问题来了，我们这个 h1 元素上面除了有默认字体大小、默认颜色等属性以外，究竟还有哪些属性呢？</p>
<img src="https://xiejie-typora.oss-cn-chengdu.aliyuncs.com/2022-08-15-014216.png" alt="image-20220815094215982" style="zoom:30%;">

<p>答案是<strong>该元素上面会有 CSS 所有的属性。</strong>你可以打开浏览器的开发者面板，选择【元素】，切换到【计算样式】，之后勾选【全部显示】，此时你就能看到在此 h1 上面所有 CSS 属性对应的值。</p>
<p><img src="https://xiejie-typora.oss-cn-chengdu.aliyuncs.com/2022-08-13-061516.png" alt="image-20220813141516153"></p>
<p>换句话说，<strong>我们所书写的任何一个 HTML 元素，实际上都有完整的一整套 CSS 样式</strong>。这一点往往是让初学者比较意外的，因为我们平时在书写 CSS 样式时，往往只会书写必要的部分，例如前面的：</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">p</span>&#123;</span><br><span class="line">  <span class="attribute">color </span>: red;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这往往会给我们造成一种错觉，认为该 p 元素上面就只有 color 属性。而真实的情况确是，任何一个 HTML 元素，都有一套完整的 CSS 样式，只不过你没有书写的样式，<strong>大概率可能</strong>会使用其默认值。例如上图中 h1 一个样式都没有设置，全部都用的默认值。</p>
<p>但是注意，我这里强调的是“大概率可能”，难道还有我们“没有设置值，但是不使用默认值”的情况么？</p>
<p>嗯，确实有的，所以我才强调你要了解“CSS 属性的计算过程”。<br><code>属性值的计算过程</code>，分为如下这么 <em>4</em> 个步骤：</p>
<ul>
<li>确定声明值</li>
<li>层叠冲突</li>
<li>使用继承</li>
<li>使用默认值</li>
</ul>
<h3 id="确定声明值"><a href="#确定声明值" class="headerlink" title="确定声明值"></a>确定声明值</h3><p>首先第一步，是确定声明值。所谓声明值就是作者自己所书写的 CSS 样式，例如前面的：</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">p</span>&#123;</span><br><span class="line">  <span class="attribute">color </span>: red;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这里我们声明了 p 元素为红色，那么就会应用此属性设置。</p>
<p>当然，除了作者样式表，一般浏览器还会存在“用户代理样式表”，简单来讲就是浏览器内置了一套样式表。</p>
<p><img src="https://xiejie-typora.oss-cn-chengdu.aliyuncs.com/2022-08-13-063500.png" alt="image-20220813143500066"></p>
<p>在上面的示例中，作者样式表中设置了 color 属性，而用户代理样式表（浏览器提供的样式表）中设置了诸如 display、margin-block-start、margin-block-end、margin-inline-start、margin-inline-end 等属性对应的值。</p>
<p>这些值目前来讲也没有什么冲突，因此最终就会应用这些属性值。</p>
<h3 id="层叠冲突"><a href="#层叠冲突" class="headerlink" title="层叠冲突"></a>层叠冲突</h3><p>在确定声明值时，可能出现一种情况，那就是声明的样式规则发生了冲突。</p>
<p>此时会进入解决层叠冲突的流程。而这一步又可以细分为下面这三个步骤：</p>
<ul>
<li>比较源的重要性</li>
<li>比较优先级</li>
<li>比较次序</li>
</ul>
<p>来来来，我们一步一步来看。</p>
<h4 id="比较源的重要性"><a href="#比较源的重要性" class="headerlink" title="比较源的重要性"></a>比较源的重要性</h4><p>当不同的 CSS 样式来源拥有相同的声明时，此时就会根据样式表来源的重要性来确定应用哪一条样式规则。</p>
<p>那么问题来了，咱们的样式表的源究竟有几种呢？</p>
<p>整体来讲有三种来源：</p>
<ul>
<li>浏览器会有一个基本的样式表来给任何网页设置默认样式。这些样式统称<strong>用户代理样式</strong>。</li>
<li>网页的作者可以定义文档的样式，这是最常见的样式表，称之为<strong>页面作者样式</strong>。</li>
<li>浏览器的用户，可以使用自定义样式表定制使用体验，称之为<strong>用户样式</strong>。</li>
</ul>
<p>对应的重要性顺序依次为：页面作者样式 &gt; 用户样式 &gt; 用户代理样式</p>
<p>更详细的来源重要性比较，可以参阅 <em>MDN</em>：*<a target="_blank" rel="noopener" href="https://developer.mozilla.org/zh-CN/docs/Web/CSS/Cascade">https://developer.mozilla.org/zh-CN/docs/Web/CSS/Cascade</a>*</p>
<p>我们来看一个示例。</p>
<p>例如现在有<strong>页面作者样式表</strong>和<strong>用户代理样式表</strong>中存在属性的冲突，那么会以作者样式表优先。</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">p</span>&#123;</span><br><span class="line">  <span class="attribute">color </span>: red;</span><br><span class="line">  <span class="attribute">display</span>: inline-block;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><img src="https://xiejie-typora.oss-cn-chengdu.aliyuncs.com/2022-08-13-064222.png" alt="image-20220813144222152"></p>
<p>可以明显的看到，作者样式表和用户代理样式表中同时存在的 display 属性的设置，最终作者样式表干掉了用户代理样式表中冲突的属性。这就是第一步，根据不同源的重要性来决定应用哪一个源的样式。</p>
<h4 id="比较优先级"><a href="#比较优先级" class="headerlink" title="比较优先级"></a>比较优先级</h4><p>那么接下来，如果是在在同一个源中有样式声明冲突怎么办呢？此时就会进行样式声明的优先级比较。</p>
<p>例如：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;test&quot;</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">h1</span>&gt;</span>test<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure>

<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.test</span> <span class="selector-tag">h1</span>&#123;</span><br><span class="line">  <span class="attribute">font-size</span>: <span class="number">50px</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-tag">h1</span> &#123;</span><br><span class="line">  <span class="attribute">font-size</span>: <span class="number">20px</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>在上面的代码中，同属于<strong>页面作者样式</strong>，源的重要性是相同的，此时会以选择器的权重来比较重要性。</p>
<p>很明显，上面的选择器的权重要大于下面的选择器，因此最终标题呈现为 <em>50px</em>。</p>
<img src="https://xiejie-typora.oss-cn-chengdu.aliyuncs.com/2021-09-16-071546.png" alt="image-20210916151546500" style="zoom: 40%;">

<p>可以看到，落败的作者样式在 <em>Elements&gt;Styles</em> 中会被划掉。</p>
<p>有关选择器权重的计算方式，不清楚的同学，可以进入此传送门：*<a target="_blank" rel="noopener" href="https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity">https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity</a>*</p>
<h4 id="比较次序"><a href="#比较次序" class="headerlink" title="比较次序"></a>比较次序</h4><p>经历了上面两个步骤，大多数的样式声明能够被确定下来。但是还剩下最后一种情况，那就是样式声明既是同源，权重也相同。</p>
<p>此时就会进入第三个步骤，比较样式声明的次序。</p>
<p>举个例子：</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">h1</span> &#123;</span><br><span class="line">  <span class="attribute">font-size</span>: <span class="number">50px</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="selector-tag">h1</span> &#123;</span><br><span class="line">  <span class="attribute">font-size</span>: <span class="number">20px</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>在上面的代码中，同样都是<strong>页面作者样式</strong>，<strong>选择器的权重也相同</strong>，此时位于下面的样式声明会层叠掉上面的那一条样式声明，最终会应用 <em>20px</em> 这一条属性值。</p>
<p><img src="https://xiejie-typora.oss-cn-chengdu.aliyuncs.com/2022-08-23-103928.png" alt="image-20220823183928330"></p>
<p>至此，样式声明中存在冲突的所有情况，就全部被解决了。</p>
<h3 id="使用继承"><a href="#使用继承" class="headerlink" title="使用继承"></a>使用继承</h3><p>层叠冲突这一步完成后，解决了相同元素被声明了多条样式规则究竟应用哪一条样式规则的问题。</p>
<p>那么如果没有声明的属性呢？此时就使用默认值么？</p>
<p><em>No、No、No</em>，别急，此时还有第三个步骤，那就是使用继承而来的值。</p>
<p>例如：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">div</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">p</span>&gt;</span>Lorem ipsum dolor sit amet.<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure>

<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">div</span> &#123;</span><br><span class="line">  <span class="attribute">color</span>: red;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>在上面的代码中，我们针对 div 设置了 color 属性值为红色，而针对 p 元素我们没有声明任何的属性，但是由于 color 是可以继承的，因此 p 元素从最近的 div 身上继承到了 color 属性的值。</p>
<p><img src="https://xiejie-typora.oss-cn-chengdu.aliyuncs.com/2022-08-13-065102.png" alt="image-20220813145102293"></p>
<p>这里有两个点需要同学们注意一下。</p>
<p>首先第一个是我强调了是<strong>最近的</strong> div 元素，看下面的例子：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">&quot;test&quot;</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">p</span>&gt;</span>Lorem ipsum dolor sit amet.<span class="tag">&lt;/<span class="name">p</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></table></figure>

<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">div</span> &#123;</span><br><span class="line">  <span class="attribute">color</span>: red;</span><br><span class="line">&#125;</span><br><span class="line"><span class="selector-class">.test</span>&#123;</span><br><span class="line">  <span class="attribute">color</span>: blue;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><img src="https://xiejie-typora.oss-cn-chengdu.aliyuncs.com/2022-08-13-065653.png" alt="image-20220813145652726"></p>
<p>因为这里并不涉及到选中 p 元素声明 color 值，而是从父元素上面继承到 color 对应的值，因此这里是<strong>谁近就听谁</strong>的，初学者往往会产生混淆，又去比较权重，但是这里根本不会涉及到权重比较，因为压根儿就没有选中到 p 元素。</p>
<p>第二个就是哪些属性能够继承？</p>
<p>关于这一点的话，大家可以在 MDN 上面很轻松的查阅到。例如我们以 text-align 为例，如下图所示：</p>
<p><img src="https://xiejie-typora.oss-cn-chengdu.aliyuncs.com/2022-08-13-070148.png" alt="image-20220813150147885"></p>
<h3 id="使用默认值"><a href="#使用默认值" class="headerlink" title="使用默认值"></a>使用默认值</h3><p>好了，目前走到这一步，如果属性值都还不能确定下来，那么就只能是使用默认值了。</p>
<p>如下图所示：</p>
<p><img src="https://xiejie-typora.oss-cn-chengdu.aliyuncs.com/2022-08-13-070825.png" alt="image-20220813150824752"></p>
<p>前面我们也说过，一个 HTML 元素要在浏览器中渲染出来，必须具备所有的 CSS 属性值，但是绝大部分我们是不会去设置的，用户代理样式表里面也不会去设置，也无法从继承拿到，因此最终都是用默认值。</p>
<h2 id="如何理解BFC"><a href="#如何理解BFC" class="headerlink" title="如何理解BFC"></a>如何理解BFC</h2><p>Box:Box 是 CSS 布局的对象和基本单位，一个页面是由很多个 Box 组成的，这个 Box 就是我们常说的盒模型<br>Block Formatting Cotext:块级上下文格式化，它是页面中的一块渲染区域，并且有一套渲染规则，它决定了其子元素将如何定位以及和其他元素的关系和相互作用<br>块级格式化上下文(Block Formatting Cotext，BFC)是 Web 页面的可视化 CSS 渲染的一部分，是布局过程中生成块级盒子的区域，也是浮动元素与其他元素的交互限定区域。<br>通俗来讲：BFC 是一个独立的布局环境，可以理解为一个容器，在这个容器中按照一定规则进行物品摆放，并不会影响其他环境中的物品。如果一个元素符合触发 BFC 的条件，则 BFC 中的元素布局不受外部影响</p>
<p>创建 BFC 的条件<br>根元素：Body；</p>
<p>元素设置浮动：float 除 none 以外的值；</p>
<p>元素设置绝对定位：position（absolute、fixed）；</p>
<p>display 值为：inline-block、table-cell、table-caption、flex 等</p>
<p>overflow 值为：hidden、auto、scroll</p>
<p>BFC 的特点：<br>垂直方向，自上而下排列，和文档流的排列方式一直<br>在 BFC 中上下相邻的两个容器的 margin 会重叠<br>计算 BFC 的高度时，需要计算浮动元素的高度<br>BFC 区域不会与浮动的容器发生重叠<br>BFC 是独立的容器，容器内部元素不会影响外部元素<br>每个元素的左 margin 值和容器的左 border 相接触</p>
<p>BFC 的作用<br>解决margin的重叠问题：由于 BFC 是一个独立的区域，内部的元素和外部的元素互不影响，将两个元素变为两个 BFC，就解决了 margin 重叠的问题。</p>
<p>解决高度塌陷的问题：在对子元素设置浮动之后，父元素会发生高度塌陷，也就是父元素的高度为 0，解决这个问题，只需要把父元素变成一个 BFC。常用的办法是给父元素设置overflow：hidden。</p>
<p>创建自适应两栏布局：可以用来创建自适应两栏布局：左边的宽度固定，右边的宽度自适应。</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.left</span> &#123;</span><br><span class="line"><span class="attribute">width</span>: <span class="number">100px</span>;</span><br><span class="line"><span class="attribute">height</span>: <span class="number">200px</span>;</span><br><span class="line"><span class="attribute">background</span>: red;</span><br><span class="line"><span class="attribute">float</span>: left;</span><br><span class="line">&#125;</span><br><span class="line"><span class="selector-class">.right</span>&#123;</span><br><span class="line"><span class="attribute">height</span>:<span class="number">300px</span>;</span><br><span class="line"><span class="attribute">background</span>:blue;</span><br><span class="line"><span class="attribute">overflow</span>:hidden;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 左侧设置float：left，右侧设置overflow：hidden。这样右边就触发BFC，BFC的区域不会与浮动元素发生重叠，所以两侧就不会发生重叠，实现自适应两栏布局 */</span></span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h2 id="惊艳一时的-CSS-属性"><a href="#惊艳一时的-CSS-属性" class="headerlink" title="惊艳一时的 CSS 属性"></a>惊艳一时的 CSS 属性</h2><ul>
<li><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/52BXTOtw_lj0agg-N87vqw">惊艳一时的 CSS 属性</a></li>
<li><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/Wzy6mlwmXscEgX91G0LvcA">CSS 视口单位：svh、lvh、dvh！</a></li>
<li><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/j7GCzc_rkuauvTU1tuNBwA">图片的性能优化及体验优化</a></li>
</ul>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kity@2.0.4/dist/kity.min.js"></script><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kityminder-core@1.4.50/dist/kityminder.core.min.js"></script><script defer="true" type="text/javascript" src="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.js"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.css">
      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="https://gz1234.gitee.io/2020/10/29/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E9%A2%98/htm%E9%9D%A2%E8%AF%95%E9%A2%98/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="郭泽">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="郭泽">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | 郭泽">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2020/10/29/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E9%A2%98/htm%E9%9D%A2%E8%AF%95%E9%A2%98/" class="post-title-link" itemprop="url">html面试题</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2020-10-29 11:09:28" itemprop="dateCreated datePublished" datetime="2020-10-29T11:09:28+08:00">2020-10-29</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2023-04-03 09:42:00" itemprop="dateModified" datetime="2023-04-03T09:42:00+08:00">2023-04-03</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E9%A2%98/" itemprop="url" rel="index"><span itemprop="name">前端面试题</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h2 id="头部meta标签"><a href="#头部meta标签" class="headerlink" title="头部meta标签"></a>头部meta标签</h2><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;!DOCTYPE <span class="meta-keyword">html</span>&gt;</span> <span class="comment">&lt;!-- 使用 HTML5 doctype，不区分大小写 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span> <span class="attr">lang</span>=<span class="string">&quot;zh-cmn-Hans&quot;</span>&gt;</span> <span class="comment">&lt;!-- 更加标准的 lang 属性写法 http://zhi.hu/XyIa --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 声明文档使用的字符编码 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">&#x27;utf-8&#x27;</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 页面内容描述 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">name</span>=<span class="string">&quot;description&quot;</span> <span class="attr">content</span>=<span class="string">&quot;不超过150个字符&quot;</span>/&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 页面关键词 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">name</span>=<span class="string">&quot;keywords&quot;</span> <span class="attr">content</span>=<span class="string">&quot;&quot;</span>/&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 网页作者 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">name</span>=<span class="string">&quot;author&quot;</span> <span class="attr">content</span>=<span class="string">&quot;高江华, g598670138@163.com&quot;</span>/&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 搜索引擎抓取</span></span><br><span class="line"><span class="comment">        1.none : 搜索引擎将忽略此网页，等价于noindex，nofollow。</span></span><br><span class="line"><span class="comment">        2.noindex : 搜索引擎不索引此网页。</span></span><br><span class="line"><span class="comment">        3.nofollow: 搜索引擎不继续通过此网页的链接索引搜索其它的网页。</span></span><br><span class="line"><span class="comment">        4.all : 搜索引擎将索引此网页与继续通过此网页的链接索引，等价于index，follow。</span></span><br><span class="line"><span class="comment">        5.index : 搜索引擎索引此网页。</span></span><br><span class="line"><span class="comment">        6.follow : 搜索引擎继续通过此网页的链接索引搜索其它的网页。</span></span><br><span class="line"><span class="comment">    --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">name</span>=<span class="string">&quot;robots&quot;</span> <span class="attr">content</span>=<span class="string">&quot;index,follow&quot;</span>/&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 为移动设备添加 viewport</span></span><br><span class="line"><span class="comment">        `width=device-width` 会导致 iPhone 5 添加到主屏后以 WebApp 全屏模式打开页面时出现黑边 http://bigc.at/ios-webapp-viewport-meta.orz </span></span><br><span class="line"><span class="comment">    --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">name</span>=<span class="string">&quot;viewport&quot;</span> <span class="attr">content</span>=<span class="string">&quot;initial-scale=1, maximum-scale=3, minimum-scale=1, user-scalable=no&quot;</span> /&gt;</span></span><br><span class="line"> </span><br><span class="line">    <span class="comment">&lt;!-- iOS 设备 begin --&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 添加到主屏后的标题（iOS 6 新增） --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">name</span>=<span class="string">&quot;apple-mobile-web-app-title&quot;</span> <span class="attr">content</span>=<span class="string">&quot;标题&quot;</span> /&gt;</span></span><br><span class="line">     <span class="comment">&lt;!-- 是否启用 WebApp 全屏模式，删除苹果默认的工具栏和菜单栏 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">name</span>=<span class="string">&quot;apple-mobile-web-app-capable&quot;</span> <span class="attr">content</span>=<span class="string">&quot;yes&quot;</span>/&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 添加智能 App 广告条 Smart App Banner（iOS 6+ Safari） --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">name</span>=<span class="string">&quot;apple-itunes-app&quot;</span> <span class="attr">content</span>=<span class="string">&quot;app-id=myAppStoreID, affiliate-data=myAffiliateData, app-argument=myURL&quot;</span> /&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 设置苹果工具栏颜色 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">name</span>=<span class="string">&quot;apple-mobile-web-app-status-bar-style&quot;</span> <span class="attr">content</span>=<span class="string">&quot;black&quot;</span>/&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 忽略页面中的数字识别为电话，忽略email识别 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">name</span>=<span class="string">&quot;format-detection&quot;</span> <span class="attr">content</span>=<span class="string">&quot;telphone=no, email=no&quot;</span>/&gt;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">&lt;!-- 双核浏览器渲染方式</span></span><br><span class="line"><span class="comment">        webkit //默认webkit内核</span></span><br><span class="line"><span class="comment">        ie-comp //默认IE兼容模式</span></span><br><span class="line"><span class="comment">        ie-stand //默认IE标准模式</span></span><br><span class="line"><span class="comment">    --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">name</span>=<span class="string">&quot;renderer&quot;</span> <span class="attr">content</span>=<span class="string">&quot;webkit&quot;</span> /&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 针对手持设备优化，主要是针对一些老的不识别viewport的浏览器，比如黑莓 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">name</span>=<span class="string">&quot;HandheldFriendly&quot;</span> <span class="attr">content</span>=<span class="string">&quot;true&quot;</span> /&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 微软的老式浏览器 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">name</span>=<span class="string">&quot;MobileOptimized&quot;</span> <span class="attr">content</span>=<span class="string">&quot;320&quot;</span> /&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- uc强制竖屏 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">name</span>=<span class="string">&quot;screen-orientation&quot;</span> <span class="attr">content</span>=<span class="string">&quot;portrait&quot;</span> /&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- QQ强制竖屏 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">name</span>=<span class="string">&quot;x5-orientation&quot;</span> <span class="attr">content</span>=<span class="string">&quot;portrait&quot;</span> /&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- UC强制全屏 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">name</span>=<span class="string">&quot;full-screen&quot;</span> <span class="attr">content</span>=<span class="string">&quot;yes&quot;</span> /&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- QQ强制全屏 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">name</span>=<span class="string">&quot;x5-fullscreen&quot;</span> <span class="attr">content</span>=<span class="string">&quot;true&quot;</span> /&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- UC应用模式 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">name</span>=<span class="string">&quot;browsermode&quot;</span> <span class="attr">content</span>=<span class="string">&quot;application&quot;</span> /&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- QQ应用模式 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">name</span>=<span class="string">&quot;x5-page-mode&quot;</span> <span class="attr">content</span>=<span class="string">&quot;app&quot;</span> /&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- windows phone 点击无高光 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">name</span>=<span class="string">&quot;msapplication-tap-highlight&quot;</span> <span class="attr">content</span>=<span class="string">&quot;no&quot;</span> /&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 用于标明网页是什么软件做的 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">name</span>=<span class="string">&quot;generator&quot;</span> <span class="attr">content</span>=<span class="string">&quot;Sublime Text3&quot;</span> /&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 用于标注版权信息 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">name</span>=<span class="string">&quot;copyright&quot;</span> <span class="attr">content</span>=<span class="string">&quot;高江华&quot;</span> /&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 搜索引擎爬虫重访时间</span></span><br><span class="line"><span class="comment">        如果页面不是经常更新，为了减轻搜索引擎爬虫对服务器带来的压力，可以设置一个爬虫的重访时间。如果重访时间过短，爬虫将按它们定义的默认时间来访问</span></span><br><span class="line"><span class="comment">    --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">name</span>=<span class="string">&quot;revisit-after&quot;</span> <span class="attr">content</span>=<span class="string">&quot;7 days&quot;</span> /&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">&lt;!-- [ http-equiv ] --&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 浏览器采取何种版本渲染当前页面(优先使用 IE 最新版本和 Chrome) --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">http-equiv</span>=<span class="string">&quot;X-UA-Compatible&quot;</span> <span class="attr">content</span>=<span class="string">&quot;IE=edge,chrome=1&quot;</span>/&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 避免IE使用兼容模式 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">http-equiv</span>=<span class="string">&quot;X-UA-Compatible&quot;</span> <span class="attr">content</span>=<span class="string">&quot;IE=edge&quot;</span> /&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 不让百度转码 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">http-equiv</span>=<span class="string">&quot;Cache-Control&quot;</span> <span class="attr">content</span>=<span class="string">&quot;no-siteapp&quot;</span> /&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 设定网页字符集(推荐使用HTML5的方式)</span></span><br><span class="line"><span class="comment">        下面是旧的HTML字符集配置，不推荐</span></span><br><span class="line"><span class="comment">    --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">http-equiv</span>=<span class="string">&quot;content-Type&quot;</span> <span class="attr">content</span>=<span class="string">&quot;text/html;charset=utf-8&quot;</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 指定请求和响应遵循的缓存机制</span></span><br><span class="line"><span class="comment">        no-cache: 先发送请求，与服务器确认该资源是否被更改，如果未被更改，则使用缓存。</span></span><br><span class="line"><span class="comment">        no-store: 不允许缓存，每次都要去服务器上，下载完整的响应。（安全措施）</span></span><br><span class="line"><span class="comment">        public : 缓存所有响应，但并非必须。因为max-age也可以做到相同效果</span></span><br><span class="line"><span class="comment">        private : 只为单个用户缓存，因此不允许任何中继进行缓存。（比如说CDN就不允许缓存private的响应）</span></span><br><span class="line"><span class="comment">        maxage : 表示当前请求开始，该响应在多久内能被缓存和重用，而不去服务器重新请求。例如：max-age=60表示响应可以再缓存和重用 60 秒。</span></span><br><span class="line"><span class="comment">    --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">http-equiv</span>=<span class="string">&quot;cache-control&quot;</span> <span class="attr">content</span>=<span class="string">&quot;no-cache&quot;</span> /&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 禁止百度自动转码 </span></span><br><span class="line"><span class="comment">        用于禁止当前页面在移动端浏览时，被百度自动转码。虽然百度的本意是好的，但是转码效果很多时候却不尽人意。所以可以在head中加入例子中的那句话，就可以避免百度自动转码了</span></span><br><span class="line"><span class="comment">    --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">http-equiv</span>=<span class="string">&quot;Cache-Control&quot;</span> <span class="attr">content</span>=<span class="string">&quot;no-siteapp&quot;</span> /&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 网页到期时间(用于设定网页的到期时间，过期后网页必须到服务器上重新传输) --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">http-equiv</span>=<span class="string">&quot;expires&quot;</span> <span class="attr">content</span>=<span class="string">&quot;Sunday 26 October 2016 01:00 GMT&quot;</span> /&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 自动刷新并指向某页面(网页将在设定的时间内，自动刷新并调向设定的网址) </span></span><br><span class="line"><span class="comment">        以下是2秒后跳转到指定网页</span></span><br><span class="line"><span class="comment">    --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">http-equiv</span>=<span class="string">&quot;refresh&quot;</span> <span class="attr">content</span>=<span class="string">&quot;2；URL=http://gaojianghua.cn/&quot;</span> /&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- cookie设定 (如果网页过期。那么这个网页存在本地的cookies也会被自动删除)</span></span><br><span class="line"><span class="comment">        &lt;meta http-equiv=&quot;Set-Cookie&quot; content=&quot;name, date&quot; /&gt; //格式</span></span><br><span class="line"><span class="comment">    --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">http-equiv</span>=<span class="string">&quot;Set-Cookie&quot;</span> <span class="attr">content</span>=<span class="string">&quot;User=gaojianghua; path=/; expires=Sunday, 10-Jan-16 10:00:00 GMT&quot;</span> /&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 启动DNS预解析, 以及src与href属性的预解析--&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">http-equiv</span>=<span class="string">&quot;x-dns-prefetch-control&quot;</span> <span class="attr">content</span>=<span class="string">&quot;on&quot;</span> /&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">&lt;!-- [ rel ] --&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- iOS 图标 begin --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;apple-touch-icon-precomposed&quot;</span> <span class="attr">href</span>=<span class="string">&quot;/apple-touch-icon-57x57-precomposed.png&quot;</span>/&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- iPhone 和 iTouch，默认 57x57 像素，必须有 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;apple-touch-icon-precomposed&quot;</span> <span class="attr">sizes</span>=<span class="string">&quot;114x114&quot;</span> <span class="attr">href</span>=<span class="string">&quot;/apple-touch-icon-114x114-precomposed.png&quot;</span>/&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- Retina iPhone 和 Retina iTouch，114x114 像素，可以没有，但推荐有 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;apple-touch-icon-precomposed&quot;</span> <span class="attr">sizes</span>=<span class="string">&quot;144x144&quot;</span> <span class="attr">href</span>=<span class="string">&quot;/apple-touch-icon-144x144-precomposed.png&quot;</span>/&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- Retina iPad，144x144 像素，可以没有，但推荐有 --&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- iOS 图标 end --&gt;</span></span><br><span class="line"> </span><br><span class="line">    <span class="comment">&lt;!-- iOS 启动画面 begin --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;apple-touch-startup-image&quot;</span> <span class="attr">sizes</span>=<span class="string">&quot;768x1004&quot;</span> <span class="attr">href</span>=<span class="string">&quot;/splash-screen-768x1004.png&quot;</span>/&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- iPad 竖屏 768 x 1004（标准分辨率） --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;apple-touch-startup-image&quot;</span> <span class="attr">sizes</span>=<span class="string">&quot;1536x2008&quot;</span> <span class="attr">href</span>=<span class="string">&quot;/splash-screen-1536x2008.png&quot;</span>/&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- iPad 竖屏 1536x2008（Retina） --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;apple-touch-startup-image&quot;</span> <span class="attr">sizes</span>=<span class="string">&quot;1024x748&quot;</span> <span class="attr">href</span>=<span class="string">&quot;/Default-Portrait-1024x748.png&quot;</span>/&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- iPad 横屏 1024x748（标准分辨率） --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;apple-touch-startup-image&quot;</span> <span class="attr">sizes</span>=<span class="string">&quot;2048x1496&quot;</span> <span class="attr">href</span>=<span class="string">&quot;/splash-screen-2048x1496.png&quot;</span>/&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- iPad 横屏 2048x1496（Retina） --&gt;</span></span><br><span class="line"> </span><br><span class="line">    <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;apple-touch-startup-image&quot;</span> <span class="attr">href</span>=<span class="string">&quot;/splash-screen-320x480.png&quot;</span>/&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- iPhone/iPod Touch 竖屏 320x480 (标准分辨率) --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;apple-touch-startup-image&quot;</span> <span class="attr">sizes</span>=<span class="string">&quot;640x960&quot;</span> <span class="attr">href</span>=<span class="string">&quot;/splash-screen-640x960.png&quot;</span>/&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- iPhone/iPod Touch 竖屏 640x960 (Retina) --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;apple-touch-startup-image&quot;</span> <span class="attr">sizes</span>=<span class="string">&quot;640x1136&quot;</span> <span class="attr">href</span>=<span class="string">&quot;/splash-screen-640x1136.png&quot;</span>/&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- iPhone 5/iPod Touch 5 竖屏 640x1136 (Retina) --&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- iOS 启动画面 end --&gt;</span></span><br><span class="line"> </span><br><span class="line">    <span class="comment">&lt;!-- iOS 设备 end --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">name</span>=<span class="string">&quot;msapplication-TileColor&quot;</span> <span class="attr">content</span>=<span class="string">&quot;#000&quot;</span>/&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- Windows 8 磁贴颜色 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">name</span>=<span class="string">&quot;msapplication-TileImage&quot;</span> <span class="attr">content</span>=<span class="string">&quot;icon.png&quot;</span>/&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- Windows 8 磁贴图标 --&gt;</span></span><br><span class="line"> </span><br><span class="line">    <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;alternate&quot;</span> <span class="attr">type</span>=<span class="string">&quot;application/rss+xml&quot;</span> <span class="attr">title</span>=<span class="string">&quot;RSS&quot;</span> <span class="attr">href</span>=<span class="string">&quot;/rss.xml&quot;</span>/&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 添加 RSS 订阅 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;shortcut icon&quot;</span> <span class="attr">type</span>=<span class="string">&quot;image/ico&quot;</span> <span class="attr">href</span>=<span class="string">&quot;/favicon.ico&quot;</span>/&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 添加 favicon icon --&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">&lt;!-- sns 社交标签 begin</span></span><br><span class="line"><span class="comment">        Open Graph Protocol，是 Facebook 制订的一个社交网络分享协议，有了上面的内容，分享之后会带上更多的信息、展示图片等，让分享的内容更吸引人</span></span><br><span class="line"><span class="comment">     --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">property</span>=<span class="string">&quot;og:type&quot;</span> <span class="attr">content</span>=<span class="string">&quot;website&quot;</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">property</span>=<span class="string">&quot;og:url&quot;</span> <span class="attr">content</span>=<span class="string">&quot;https://gaojianghua.cn/docs/&quot;</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">property</span>=<span class="string">&quot;og:title&quot;</span> <span class="attr">content</span>=<span class="string">&quot;Wolffy-Document&quot;</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">property</span>=<span class="string">&quot;og:image&quot;</span> <span class="attr">content</span>=<span class="string">&quot;https://gaojianghua.oss-cn-hangzhou.aliyuncs.com/home/%E7%81%B0%E5%A4%AA%E7%8B%BC.png&quot;</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">property</span>=<span class="string">&quot;og:description&quot;</span> <span class="attr">content</span>=<span class="string">&quot;技术博客--前端后端运维知识点收录: Vue, React, Taro, ReactNative, Webpack, Vite, UniApp, 小程序, H5, Docker, GitGoLang, Node, Nest, Mysql, Redis, 数据结构, 算法&quot;</span> /&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- sns 社交标签 end --&gt;</span></span><br><span class="line"> </span><br><span class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span>标题<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br></pre></td></tr></table></figure>

<h2 id="头部link标签"><a href="#头部link标签" class="headerlink" title="头部link标签"></a>头部link标签</h2><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;!DOCTYPE <span class="meta-keyword">html</span>&gt;</span> <span class="comment">&lt;!-- 使用 HTML5 doctype，不区分大小写 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span> <span class="attr">lang</span>=<span class="string">&quot;zh-cmn-Hans&quot;</span>&gt;</span> <span class="comment">&lt;!-- 更加标准的 lang 属性写法 http://zhi.hu/XyIa --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- [link] </span></span><br><span class="line"><span class="comment">        属性: </span></span><br><span class="line"><span class="comment">        href：指明外部资源文件的路径，即告诉浏览器外部资源的位置</span></span><br><span class="line"><span class="comment">        hreflang：说明外部资源使用的语言</span></span><br><span class="line"><span class="comment">        media：说明外部资源用于哪种设备</span></span><br><span class="line"><span class="comment">        rel：必填，表明当前文档和外部资源的关系</span></span><br><span class="line"><span class="comment">        sizes：指定图标的大小，只对属性rel=&quot;icon&quot;生效</span></span><br><span class="line"><span class="comment">        type：说明外部资源的 MIME 类型，如text/css、image/x-icon</span></span><br><span class="line"><span class="comment">    --&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- title属性会控制css样式文件的加载方式</span></span><br><span class="line"><span class="comment">        无title属性：ref=stylesheet时css样式始终都会加载并渲染</span></span><br><span class="line"><span class="comment">        有title属性：ref=stylesheet时css样式会作为默认样式加载并渲染</span></span><br><span class="line"><span class="comment">        有title属性：ref=alternate stylesheet时css样式会作为预备样式渲染，默认不加载</span></span><br><span class="line"><span class="comment">     --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;stylesheet&quot;</span> <span class="attr">type</span>=<span class="string">&quot;text/css&quot;</span> <span class="attr">href</span>=<span class="string">&quot;./index.css&quot;</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- alternate (用于主题样式切换，将css作为预备样式，通过对link使用disabled进行切换) --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;alternate stylesheet&quot;</span> <span class="attr">type</span>=<span class="string">&quot;text/css&quot;</span> <span class="attr">href</span>=<span class="string">&quot;./index.css&quot;</span> <span class="attr">title</span>=<span class="string">&quot;gaojianghua&quot;</span> /&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- alternate (也可以做处于移动端访问时跳转H5的网页) --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;alternate&quot;</span> <span class="attr">media</span>=<span class="string">&quot;only screen and (max-width:750px)&quot;</span> <span class="attr">href</span>=<span class="string">&quot;https://gaojianghua.cn/docs/&quot;</span> /&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 指定网页的规范版本，搜索引擎则会把权重集中到规范版本页面，由此提升网页的权重，排名更加靠前</span></span><br><span class="line"><span class="comment">        可指定为PC端网页, 区分H5移动端</span></span><br><span class="line"><span class="comment">    --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;canonical&quot;</span> <span class="attr">href</span>=<span class="string">&quot;https://gaojianghua.cn/docs/&quot;</span> /&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- DNS预解析 (link方式的DNS的预解析与页面的加载是并行处理的，不会影响到页面的加载性能) --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;dns-prefetch&quot;</span> <span class="attr">href</span>=<span class="string">&quot;https://gaojianghua.cn/docs/&quot;</span> /&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 链接到外部，告知浏览器，此链接非本站链接 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;external&quot;</span> <span class="attr">href</span>=<span class="string">&quot;&quot;</span> /&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 链接到集合中的首个文档 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;first&quot;</span> <span class="attr">href</span>=<span class="string">&quot;&quot;</span> /&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 链接帮助信息 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;help&quot;</span> <span class="attr">href</span>=<span class="string">&quot;&quot;</span> /&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 定义网站或网页在浏览器标题栏中的图标 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;icon&quot;</span> <span class="attr">href</span>=<span class="string">&quot;favicon.ico&quot;</span> /&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 链接到文档的版权信息，即文档的版权声明 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;license&quot;</span> <span class="attr">href</span>=<span class="string">&quot;&quot;</span> /&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 链接到集合中的末尾文档 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;last&quot;</span> <span class="attr">href</span>=<span class="string">&quot;&quot;</span> /&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 指定页面不被搜索引擎跟踪，或者此页面不被搜索引擎爬取 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;nofollow&quot;</span> <span class="attr">href</span>=<span class="string">&quot;&quot;</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">&quot;https://gaojianghua.cn/docs/&quot;</span> <span class="attr">rel</span>=<span class="string">&quot;nofollow&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">a</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 用于记录文档的下一页，可提示浏览器文章的开始URL，且浏览器可提前加载此页 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;start&quot;</span> <span class="attr">href</span>=<span class="string">&quot;https://gaojianghua.cn/docs/&quot;</span> /&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 阻止浏览器发送访问来源信息 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;noreferrer&quot;</span> <span class="attr">href</span>=<span class="string">&quot;&quot;</span> /&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 在页面渲染之前对资源进行预加载，且不易阻塞页面的初步渲染</span></span><br><span class="line"><span class="comment">        其中href和as属性用于指定被加载资源的路径和类型，as指定资源的类型后，浏览器可以更加精确地优化资源加载优先级。</span></span><br><span class="line"><span class="comment">        as属性值:</span></span><br><span class="line"><span class="comment">        audio: 音频文件。</span></span><br><span class="line"><span class="comment">        document: 一个将要被嵌入到[&lt;frame&gt;]或[&lt;iframe&gt;]内部的HTML文档。</span></span><br><span class="line"><span class="comment">        embed: 一个将要被嵌入到[&lt;embed&gt;]元素内部的资源。</span></span><br><span class="line"><span class="comment">        fetch: 那些将要通过fetch和XHR请求来获取的资源，比如一个ArrayBuffer或JSON文件。</span></span><br><span class="line"><span class="comment">        font: 字体文件。</span></span><br><span class="line"><span class="comment">        image: 图片文件。</span></span><br><span class="line"><span class="comment">        object: 一个将会被嵌入到[&lt;embed&gt;]元素内的文件。</span></span><br><span class="line"><span class="comment">        script: JavaScript文件。</span></span><br><span class="line"><span class="comment">        style: 样式表。</span></span><br><span class="line"><span class="comment">        track: WebVTT文件。</span></span><br><span class="line"><span class="comment">        worker: 一个JavaScript的web worker或shared worker。</span></span><br><span class="line"><span class="comment">        video: 视频文件。</span></span><br><span class="line"><span class="comment">    --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;preload&quot;</span> <span class="attr">href</span>=<span class="string">&quot;main.js&quot;</span> <span class="attr">as</span>=<span class="string">&quot;script&quot;</span> /&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 博客中用来通知其他文章被引用的一种方式 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;pingback&quot;</span> <span class="attr">href</span>=<span class="string">&quot;https://gaojianghua.cn/docs/&quot;</span> /&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 空闲时预加载未来要使用的资源，优先级较低，一般用于加载非本页的其他页面所需要的资源，以便加快后续页面的首屏渲染速度。</span></span><br><span class="line"><span class="comment">        资源加载完成后，可以被缓存 </span></span><br><span class="line"><span class="comment">    --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;prefetch&quot;</span> <span class="attr">href</span>=<span class="string">&quot;&quot;</span> /&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 优化可能导航到的下一页上的资源的加载，在后台渲染了整个页面以及整个页面所有的资源 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;preconnect&quot;</span> <span class="attr">href</span>=<span class="string">&quot;&quot;</span>/&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 允许浏览器在一个 HTTP 请求正式发给服务器前预先执行一些操作，建立与服务器的连接</span></span><br><span class="line"><span class="comment">        其中包括DNS预解析、TLS协商、TCP握手</span></span><br><span class="line"><span class="comment">        消除了往返延迟并为用户节省了时间，以便后续可以更快地获取链接内容</span></span><br><span class="line"><span class="comment">        crossorigin: 浏览器为该模式维护一个单独的套接字池</span></span><br><span class="line"><span class="comment">    --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;preconnect&quot;</span> <span class="attr">href</span>=<span class="string">&quot;https://gaojianghua.cn/docs/&quot;</span> <span class="attr">crossorigin</span> /&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 用于记录文档的上一页 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;prev&quot;</span> <span class="attr">href</span>=<span class="string">&quot;&quot;</span> /&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 链接到文档的搜索工具 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;search&quot;</span> <span class="attr">href</span>=<span class="string">&quot;&quot;</span> /&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 指定作为样式表的外部资源，若未设置type，浏览器默认为text/css --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;stylesheet&quot;</span> <span class="attr">href</span>=<span class="string">&quot;style.css&quot;</span> /&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 指定当前文档使用的标签、关键词 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;tag&quot;</span> <span class="attr">href</span>=<span class="string">&quot;&quot;</span> /&gt;</span></span><br><span class="line">    <span class="comment">&lt;!-- 指向一个文档，此文档提供此网页的上下文关系 --&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;up&quot;</span> <span class="attr">href</span>=<span class="string">&quot;&quot;</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span>标题<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"></span><br></pre></td></tr></table></figure>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kity@2.0.4/dist/kity.min.js"></script><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kityminder-core@1.4.50/dist/kityminder.core.min.js"></script><script defer="true" type="text/javascript" src="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.js"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.css">
      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="https://gz1234.gitee.io/2020/10/29/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E9%A2%98/http%E7%BD%91%E8%90%BD%E5%8D%8F%E8%AE%AE%E9%9D%A2%E8%AF%95%E9%A2%98/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="郭泽">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="郭泽">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | 郭泽">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2020/10/29/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E9%A2%98/http%E7%BD%91%E8%90%BD%E5%8D%8F%E8%AE%AE%E9%9D%A2%E8%AF%95%E9%A2%98/" class="post-title-link" itemprop="url">http网落协议面试题</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2020-10-29 11:09:28" itemprop="dateCreated datePublished" datetime="2020-10-29T11:09:28+08:00">2020-10-29</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2023-04-27 09:11:48" itemprop="dateModified" datetime="2023-04-27T09:11:48+08:00">2023-04-27</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E9%A2%98/" itemprop="url" rel="index"><span itemprop="name">前端面试题</span></a>
        </span>
          ，
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/http/" itemprop="url" rel="index"><span itemprop="name">http</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h2 id="请求在客户端报-413-是什么错误-怎么解决呢"><a href="#请求在客户端报-413-是什么错误-怎么解决呢" class="headerlink" title="请求在客户端报 413 是什么错误,怎么解决呢?"></a>请求在客户端报 413 是什么错误,怎么解决呢?</h2><ul>
<li>HTTP 413 错误(Request entity too large 请求实体太大)，就是客户端发送的实体主体部分比服务器能够或者希望处理的要大时，会出现这样的错误。一般上传文件时会出现这样的错误概率比较大。<br>解决方案可以修改服务器的配置文件。配置客户端请求大小和缓存大小</li>
</ul>
<h2 id="quic-基-于-udp-怎-么-保-证-可-靠-性"><a href="#quic-基-于-udp-怎-么-保-证-可-靠-性" class="headerlink" title="quic 基 于 udp 怎 么 保 证 可 靠 性"></a>quic 基 于 udp 怎 么 保 证 可 靠 性</h2><h2 id="讲-一-下-同-源-策-略-和-跨-域-方-案-？-CORS-的-几-个-头-部-是-什-么-？"><a href="#讲-一-下-同-源-策-略-和-跨-域-方-案-？-CORS-的-几-个-头-部-是-什-么-？" class="headerlink" title="讲 一 下 同 源 策 略 和 跨 域 方 案 ？ CORS 的 几 个 头 部 是 什 么 ？"></a>讲 一 下 同 源 策 略 和 跨 域 方 案 ？ CORS 的 几 个 头 部 是 什 么 ？</h2><h2 id="grpc-相-比-http-的-优-势-？"><a href="#grpc-相-比-http-的-优-势-？" class="headerlink" title="grpc 相 比 http 的 优 势 ？"></a>grpc 相 比 http 的 优 势 ？</h2><h2 id="http-和-https-的基本概念"><a href="#http-和-https-的基本概念" class="headerlink" title="http 和 https 的基本概念"></a>http 和 https 的基本概念</h2><p>http: 是一个客户端和服务器端请求和应答的标准（TCP），用于从 WWW 服务器传输超文本到本地浏览器的超文本传输协议。<br>https:是以安全为目标的 HTTP 通道，即 HTTP 下 加入 SSL 层进行加密。其作用是：建立一个信息安全通道，来确保数据的传输，确保网站的真实性。</p>
<h2 id="http-和-https-的区别及优缺点？"><a href="#http-和-https-的区别及优缺点？" class="headerlink" title="http 和 https 的区别及优缺点？"></a>http 和 https 的区别及优缺点？</h2><ul>
<li>http 是超文本传输协议，信息是明文传输，HTTPS 协议要比 http 协议<code>安全</code>，https 是具有安全性的 ssl 加密传输协议，可防止数据在传输过程中被窃取、改变，确保数据的完整性(当然这种安全性并非绝对的，对于更深入的 Web 安全问题，此处暂且不表)。</li>
<li>http 协议的<code>默认端口</code>为 80，https 的默认端口为 443。</li>
<li>http 的连接很简单，是无状态的。https 握手阶段比较<code>费时</code>，会使页面加载时间延长 50%，增加 10%~20%的耗电。</li>
<li>https <code>缓存</code>不如 http 高效，会增加数据开销。</li>
<li>Https 协议需要 ca 证书，费用较高，功能越强大的<code>证书费</code>用越高。</li>
<li>SSL 证书需要绑定 <code>IP</code>，不能再同一个 IP 上绑定多个域名，IPV4 资源支持不了这种消耗。</li>
</ul>
          <!--noindex-->
            <div class="post-button">
              <a class="btn" href="/2020/10/29/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E9%A2%98/http%E7%BD%91%E8%90%BD%E5%8D%8F%E8%AE%AE%E9%9D%A2%E8%AF%95%E9%A2%98/#more" rel="contents">
                阅读全文 &raquo;
              </a>
            </div>
          <!--/noindex-->
        
      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="https://gz1234.gitee.io/2020/10/29/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E9%A2%98/js%20%E5%AE%89%E5%85%A8%E6%8C%87%E5%8D%97/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="郭泽">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="郭泽">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | 郭泽">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2020/10/29/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E9%A2%98/js%20%E5%AE%89%E5%85%A8%E6%8C%87%E5%8D%97/" class="post-title-link" itemprop="url">js 安全指南</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2020-10-29 11:09:28" itemprop="dateCreated datePublished" datetime="2020-10-29T11:09:28+08:00">2020-10-29</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2023-03-29 10:39:55" itemprop="dateModified" datetime="2023-03-29T10:39:55+08:00">2023-03-29</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E9%A2%98/" itemprop="url" rel="index"><span itemprop="name">前端面试题</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <p><a id="1"></a></p>
<h2 id="JavaScript页面类"><a href="#JavaScript页面类" class="headerlink" title="JavaScript页面类"></a>JavaScript页面类</h2><p><a id="1.1"></a></p>
<h3 id="I-代码实现"><a href="#I-代码实现" class="headerlink" title="I. 代码实现"></a>I. 代码实现</h3><p><a id="1.1.1"></a></p>
<h4 id="1-1-原生DOM-API的安全操作"><a href="#1-1-原生DOM-API的安全操作" class="headerlink" title="1.1 原生DOM API的安全操作"></a>1.1 原生DOM API的安全操作</h4><p><strong>1.1.1【必须】HTML标签操作，限定/过滤传入变量值</strong></p>
<ul>
<li>使用<code>innerHTML=</code>、<code>outerHTML=</code>、<code>document.write()</code>、<code>document.writeln()</code>时，如变量值外部可控，应对特殊字符（<code>&amp;, &lt;, &gt;, &quot;, &#39;</code>）做编码转义，或使用安全的DOM API替代，包括：<code>innerText=</code></li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 假设 params 为用户输入， text 为 DOM 节点</span></span><br><span class="line"><span class="comment">// bad：将不可信内容带入HTML标签操作</span></span><br><span class="line"><span class="keyword">const</span> &#123; user &#125; = params;</span><br><span class="line"><span class="comment">// ...</span></span><br><span class="line">text.innerHTML = <span class="string">`Follow @<span class="subst">$&#123;user&#125;</span>`</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// good: innerHTML操作前，对特殊字符编码转义</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">htmlEncode</span>(<span class="params">iStr</span>) </span>&#123;</span><br><span class="line"> <span class="keyword">let</span> sStr = iStr;</span><br><span class="line"> sStr = sStr.replace(<span class="regexp">/&amp;/g</span>, <span class="string">&quot;&amp;amp;&quot;</span>);</span><br><span class="line"> sStr = sStr.replace(<span class="regexp">/&gt;/g</span>, <span class="string">&quot;&amp;gt;&quot;</span>);</span><br><span class="line"> sStr = sStr.replace(<span class="regexp">/&lt;/g</span>, <span class="string">&quot;&amp;lt;&quot;</span>);</span><br><span class="line"> sStr = sStr.replace(<span class="regexp">/&quot;/g</span>, <span class="string">&quot;&amp;quot;&quot;</span>);</span><br><span class="line"> sStr = sStr.replace(<span class="regexp">/&#x27;/g</span>, <span class="string">&quot;&amp;#39;&quot;</span>);</span><br><span class="line"> <span class="keyword">return</span> sStr;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> &#123; user &#125; = params;</span><br><span class="line">user = htmlEncode(user);</span><br><span class="line"><span class="comment">// ...</span></span><br><span class="line">text.innerHTML = <span class="string">`Follow @<span class="subst">$&#123;user&#125;</span>`</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// good: 使用安全的DOM API替代innerHTML</span></span><br><span class="line"><span class="keyword">const</span> &#123; user &#125; = params;</span><br><span class="line"><span class="comment">// ...</span></span><br><span class="line">text.innerText = <span class="string">`Follow @<span class="subst">$&#123;user&#125;</span>`</span>;</span><br></pre></td></tr></table></figure>

<p><strong>1.1.2【必须】HTML属性操作，限定/过滤传入变量值</strong></p>
<ul>
<li><p>使用<code>element.setAttribute(name, value);</code>时，如第一个参数值<code>name</code>外部可控，应用白名单限定允许操作的属性范围。</p>
</li>
<li><p>使用<code>element.setAttribute(name, value);</code>时，操作<code>a.href</code>、<code>ifame.src</code>、<code>form.action</code>、<code>embed.src</code>、<code>object.data</code>、<code>link.href</code>、<code>area.href</code>、<code>input.formaction</code>、<code>button.formaction</code>属性时，如第二个参数值<code>value</code>外部可控，应参考<em>JavaScript页面类规范1.3.1</em>部分，限定页面重定向或引入资源的目标地址。</p>
</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// good: setAttribute操作前，限定引入资源的目标地址</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">addExternalCss</span>(<span class="params">e</span>) </span>&#123;</span><br><span class="line"> <span class="keyword">const</span> t = <span class="built_in">document</span>.createElement(<span class="string">&#x27;link&#x27;</span>);</span><br><span class="line"> t.setAttribute(<span class="string">&#x27;href&#x27;</span>, e),</span><br><span class="line"> t.setAttribute(<span class="string">&#x27;rel&#x27;</span>, <span class="string">&#x27;stylesheet&#x27;</span>),</span><br><span class="line"> t.setAttribute(<span class="string">&#x27;type&#x27;</span>, <span class="string">&#x27;text/css&#x27;</span>),</span><br><span class="line"> <span class="built_in">document</span>.head.appendChild(t)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">validURL</span>(<span class="params">sUrl</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> !!((<span class="regexp">/^(https?:\/\/)?[\w\-.]+\.(qq|tencent)\.com($|\/|\\)/i</span>).test(sUrl) || (<span class="regexp">/^[\w][\w/.\-_%]+$/i</span>).test(sUrl) || (<span class="regexp">/^[/\\][^/\\]/i</span>).test(sUrl));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> sUrl = <span class="string">&quot;https://evil.com/1.css&quot;</span></span><br><span class="line"><span class="keyword">if</span> (validURL(sUrl)) &#123;</span><br><span class="line"> addExternalCss(sUrl);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><a id="1.1.2"></a></p>
<h4 id="1-2-流行框架-库的安全操作"><a href="#1-2-流行框架-库的安全操作" class="headerlink" title="1.2 流行框架/库的安全操作"></a>1.2 流行框架/库的安全操作</h4><p><strong>1.2.1【必须】限定/过滤传入jQuery不安全函数的变量值</strong></p>
<ul>
<li>使用<code>.html()</code>、<code>.append()</code>、<code>.prepend()</code>、<code>.wrap()</code>、<code>.replaceWith()</code>、<code>.wrapAll()</code>、<code>.wrapInner()</code>、<code>.after()</code>、<code>.before()</code>时，如变量值外部可控，应对特殊字符（<code>&amp;, &lt;, &gt;, &quot;, &#39;</code>）做编码转义。</li>
<li>引入<code>jQuery 1.x（等于或低于1.12）、jQuery2.x（等于或低于2.2）</code>，且使用<code>$()</code>时，应优先考虑替换为最新版本。如一定需要使用，应对传入参数值中的特殊字符（<code>&amp;, &lt;, &gt;, &quot;, &#39;</code>）做编码转义。</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// bad：将不可信内容，带入jQuery不安全函数.after()操作</span></span><br><span class="line"><span class="keyword">const</span> &#123; user &#125; = params;</span><br><span class="line"><span class="comment">// ...</span></span><br><span class="line">$(<span class="string">&quot;p&quot;</span>).after(user);</span><br><span class="line"></span><br><span class="line"><span class="comment">// good: jQuery不安全函数.html()操作前，对特殊字符编码转义</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">htmlEncode</span>(<span class="params">iStr</span>) </span>&#123;</span><br><span class="line"> <span class="keyword">let</span> sStr = iStr;</span><br><span class="line"> sStr = sStr.replace(<span class="regexp">/&amp;/g</span>, <span class="string">&quot;&amp;amp;&quot;</span>);</span><br><span class="line"> sStr = sStr.replace(<span class="regexp">/&gt;/g</span>, <span class="string">&quot;&amp;gt;&quot;</span>);</span><br><span class="line"> sStr = sStr.replace(<span class="regexp">/&lt;/g</span>, <span class="string">&quot;&amp;lt;&quot;</span>);</span><br><span class="line"> sStr = sStr.replace(<span class="regexp">/&quot;/g</span>, <span class="string">&quot;&amp;quot;&quot;</span>);</span><br><span class="line"> sStr = sStr.replace(<span class="regexp">/&#x27;/g</span>, <span class="string">&quot;&amp;#39;&quot;</span>);</span><br><span class="line"> <span class="keyword">return</span> sStr;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// const user = params.user;</span></span><br><span class="line">user = htmlEncode(user);</span><br><span class="line"><span class="comment">// ...</span></span><br><span class="line">$(<span class="string">&quot;p&quot;</span>).html(user);</span><br></pre></td></tr></table></figure>

<ul>
<li><p>使用<code>.attr()</code>操作<code>a.href</code>、<code>ifame.src</code>、<code>form.action</code>、<code>embed.src</code>、<code>object.data</code>、<code>link.href</code>、<code>area.href</code>、<code>input.formaction</code>、<code>button.formaction</code>属性时，应参考<em>JavaScript页面类规范1.3.1</em>部分，限定重定向的资源目标地址。</p>
</li>
<li><p>使用<code>.attr(attributeName, value)</code>时，如第一个参数值<code>attributeName</code>外部可控，应用白名单限定允许操作的属性范围。</p>
</li>
<li><p>使用<code>$.getScript(url [, success ])</code>时，如第一个参数值<code>url</code>外部可控（如：从URL取值拼接，请求jsonp接口），应限定可控变量值的字符集范围为：<code>[a-zA-Z0-9_-]+</code>。</p>
</li>
</ul>
<p><strong>1.2.2【必须】限定/过滤传入Vue.js不安全函数的变量值</strong></p>
<ul>
<li>使用<code>v-html</code>时，不允许对用户提供的内容使用HTML插值。如业务需要，应先对不可信内容做富文本过滤。</li>
</ul>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">// bad：直接渲染外部传入的不可信内容</span><br><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">v-html</span>=<span class="string">&quot;userProvidedHtml&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"></span><br><span class="line">// good：使用富文本过滤库处理不可信内容后渲染</span><br><span class="line"><span class="comment">&lt;!-- 使用 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">div</span> <span class="attr">v-xss-html</span>=<span class="string">&quot;&#123;&#x27;mode&#x27;: &#x27;whitelist&#x27;, dirty: html, options: options&#125;&quot;</span> &gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">&lt;!-- 配置 --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="javascript"> <span class="keyword">new</span> Vue(&#123;</span></span><br><span class="line"><span class="javascript"> el: <span class="string">&quot;#app&quot;</span>,</span></span><br><span class="line"> data: &#123;</span><br><span class="line">  options: &#123;</span><br><span class="line">   whiteList: &#123;</span><br><span class="line"><span class="javascript">    a: [<span class="string">&quot;href&quot;</span>, <span class="string">&quot;title&quot;</span>, <span class="string">&quot;target&quot;</span>, <span class="string">&quot;class&quot;</span>, <span class="string">&quot;id&quot;</span>],</span></span><br><span class="line"><span class="javascript">    div: [<span class="string">&quot;class&quot;</span>, <span class="string">&quot;id&quot;</span>],</span></span><br><span class="line"><span class="javascript">    span: [<span class="string">&quot;class&quot;</span>, <span class="string">&quot;id&quot;</span>],</span></span><br><span class="line"><span class="javascript">    img: [<span class="string">&quot;src&quot;</span>, <span class="string">&quot;alt&quot;</span>],</span></span><br><span class="line">   &#125;,</span><br><span class="line">  &#125;,</span><br><span class="line"> &#125;,</span><br><span class="line">&#125;);</span><br><span class="line"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure>

<ul>
<li><p>使用<code>v-bind</code>操作<code>a.href</code>、<code>ifame.src</code>、<code>form.action</code>、<code>embed.src</code>、<code>object.data</code>、<code>link.href</code>、<code>area.href</code>、<code>input.formaction</code>、<code>button.formaction</code>时，应确保后端已参考<em>JavaScript页面类规范1.3.1</em>部分，限定了供前端调用的重定向目标地址。</p>
</li>
<li><p>使用<code>v-bind</code>操作<code>style</code>属性时，应只允许外部控制特定、可控的CSS属性值</p>
</li>
</ul>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">// bad：v-bind允许外部可控值，自定义CSS属性及数值</span><br><span class="line"><span class="tag">&lt;<span class="name">a</span> <span class="attr">v-bind:href</span>=<span class="string">&quot;sanitizedUrl&quot;</span> <span class="attr">v-bind:style</span>=<span class="string">&quot;userProvidedStyles&quot;</span>&gt;</span></span><br><span class="line">click me</span><br><span class="line"><span class="tag">&lt;/<span class="name">a</span>&gt;</span></span><br><span class="line"></span><br><span class="line">// good：v-bind只允许外部提供特性、可控的CSS属性值</span><br><span class="line"><span class="tag">&lt;<span class="name">a</span> <span class="attr">v-bind:href</span>=<span class="string">&quot;sanitizedUrl&quot;</span> <span class="attr">v-bind:style</span>=<span class="string">&quot;&#123;</span></span></span><br><span class="line"><span class="tag"><span class="string">color: userProvidedColor,</span></span></span><br><span class="line"><span class="tag"><span class="string">background: userProvidedBackground</span></span></span><br><span class="line"><span class="tag"><span class="string">&#125;&quot;</span> &gt;</span></span><br><span class="line">click me</span><br><span class="line"><span class="tag">&lt;/<span class="name">a</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p><a id="1.1.3"></a></p>
<h4 id="1-3-页面重定向"><a href="#1-3-页面重定向" class="headerlink" title="1.3 页面重定向"></a>1.3 页面重定向</h4><p><strong>1.3.1【必须】限定跳转目标地址</strong></p>
<ul>
<li><p>使用白名单，限定重定向地址的协议前缀（默认只允许HTTP、HTTPS）、域名（默认只允许公司根域），或指定为固定值；</p>
</li>
<li><p>适用场景包括，使用函数方法：<code>location.href</code>、<code>window.open()</code>、<code>location.assign()</code>、<code>location.replace()</code>；赋值或更新HTML属性：<code>a.href</code>、<code>ifame.src</code>、<code>form.action</code>、<code>embed.src</code>、<code>object.data</code>、<code>link.href</code>、<code>area.href</code>、<code>input.formaction</code>、<code>button.formaction</code>；</p>
</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// bad: 跳转至外部可控的不可信地址</span></span><br><span class="line"><span class="keyword">const</span> sTargetUrl = getURLParam(<span class="string">&quot;target&quot;</span>);</span><br><span class="line">location.replace(sTargetUrl);</span><br><span class="line"></span><br><span class="line"><span class="comment">// good: 白名单限定重定向地址</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">validURL</span>(<span class="params">sUrl</span>) </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> !!((<span class="regexp">/^(https?:\/\/)?[\w\-.]+\.(qq|tencent)\.com($|\/|\\)/i</span>).test(sUrl) || (<span class="regexp">/^[\w][\w/.\-_%]+$/i</span>).test(sUrl) || (<span class="regexp">/^[/\\][^/\\]/i</span>).test(sUrl));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> sTargetUrl = getURLParam(<span class="string">&quot;target&quot;</span>);</span><br><span class="line"><span class="keyword">if</span> (validURL(sTargetUrl)) &#123;</span><br><span class="line"> location.replace(sTargetUrl);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// good: 制定重定向地址为固定值</span></span><br><span class="line"><span class="keyword">const</span> sTargetUrl = <span class="string">&quot;http://www.qq.com&quot;</span>;</span><br><span class="line">location.replace(sTargetUrl);</span><br></pre></td></tr></table></figure>

<p><a id="1.1.4"></a></p>
<h4 id="1-4-JSON解析-动态执行"><a href="#1-4-JSON解析-动态执行" class="headerlink" title="1.4 JSON解析/动态执行"></a>1.4 JSON解析/动态执行</h4><p><strong>1.4.1【必须】使用安全的JSON解析方式</strong></p>
<ul>
<li>应使用<code>JSON.parse()</code>解析JSON字符串。低版本浏览器，应使用安全的<a target="_blank" rel="noopener" href="https://github.com/douglascrockford/JSON-js/blob/master/json2.js">Polyfill封装</a></li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// bad: 直接调用eval解析json</span></span><br><span class="line"><span class="keyword">const</span> sUserInput = getURLParam(<span class="string">&quot;json_val&quot;</span>);</span><br><span class="line"><span class="keyword">const</span> jsonstr1 = <span class="string">`&#123;&quot;name&quot;:&quot;a&quot;,&quot;company&quot;:&quot;b&quot;,&quot;value&quot;:&quot;<span class="subst">$&#123;sUserInput&#125;</span>&quot;&#125;`</span>;</span><br><span class="line"><span class="keyword">const</span> json1 = <span class="built_in">eval</span>(<span class="string">`(<span class="subst">$&#123;jsonstr1&#125;</span>)`</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// good: 使用JSON.parse解析</span></span><br><span class="line"><span class="keyword">const</span> sUserInput = getURLParam(<span class="string">&quot;json_val&quot;</span>);</span><br><span class="line"><span class="built_in">JSON</span>.parse(sUserInput, <span class="function">(<span class="params">k, v</span>) =&gt;</span> &#123;</span><br><span class="line"> <span class="keyword">if</span> (k === <span class="string">&quot;&quot;</span>) <span class="keyword">return</span> v;</span><br><span class="line"> <span class="keyword">return</span> v * <span class="number">2</span>;</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// good: 低版本浏览器，使用安全的Polyfill封装（基于eval）</span></span><br><span class="line">&lt;script src=<span class="string">&quot;https://github.com/douglascrockford/JSON-js/blob/master/json2.js&quot;</span>&gt;&lt;/script&gt;;</span><br><span class="line"><span class="keyword">const</span> sUserInput = getURLParam(<span class="string">&quot;json_val&quot;</span>);</span><br><span class="line"><span class="built_in">JSON</span>.parse(sUserInput);</span><br></pre></td></tr></table></figure>

<p><a id="1.1.5"></a></p>
<h4 id="1-5-跨域通讯"><a href="#1-5-跨域通讯" class="headerlink" title="1.5 跨域通讯"></a>1.5 跨域通讯</h4><p><strong>1.5.1【必须】使用安全的前端跨域通信方式</strong></p>
<ul>
<li>具有隔离登录态（如：p_skey）、涉及用户高敏感信息的业务（如：微信网页版、QQ空间、QQ邮箱、公众平台），禁止通过<code>document.domain</code>降域，实现前端跨域通讯，应使用postMessage替代。</li>
</ul>
<p><strong>1.5.2【必须】使用postMessage应限定Origin</strong></p>
<ul>
<li><p>在message事件监听回调中，应先使用<code>event.origin</code>校验来源，再执行具体操作。</p>
</li>
<li><p>校验来源时，应使用<code>===</code>判断，禁止使用<code>indexOf()</code></p>
</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// bad: 使用indexOf校验Origin值</span></span><br><span class="line"><span class="built_in">window</span>.addEventListener(<span class="string">&quot;message&quot;</span>, <span class="function">(<span class="params">e</span>) =&gt;</span> &#123;</span><br><span class="line"> <span class="keyword">if</span> (~e.origin.indexOf(<span class="string">&quot;https://a.qq.com&quot;</span>)) &#123;</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line"> &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line"> &#125;</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// good: 使用postMessage时，限定Origin，且使用===判断</span></span><br><span class="line"><span class="built_in">window</span>.addEventListener(<span class="string">&quot;message&quot;</span>, <span class="function">(<span class="params">e</span>) =&gt;</span> &#123;</span><br><span class="line"> <span class="keyword">if</span> (e.origin === <span class="string">&quot;https://a.qq.com&quot;</span>) &#123;</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line"> &#125;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<p><a id="1.2"></a></p>
<h3 id="II-配置-amp-环境"><a href="#II-配置-amp-环境" class="headerlink" title="II. 配置&amp;环境"></a>II. 配置&amp;环境</h3><p><a id="1.2.1"></a></p>
<h4 id="2-1-敏感-配置信息"><a href="#2-1-敏感-配置信息" class="headerlink" title="2.1 敏感/配置信息"></a>2.1 敏感/配置信息</h4><p><strong>2.1.1【必须】禁止明文硬编码AK/SK</strong></p>
<ul>
<li>禁止前端页面的JS明文硬编码AK/SK类密钥，应封装成后台接口，AK/SK保存在后端配置中心或密钥管理系统</li>
</ul>
<p><a id="1.2.2"></a></p>
<h4 id="2-2-第三方组件-资源"><a href="#2-2-第三方组件-资源" class="headerlink" title="2.2 第三方组件/资源"></a>2.2 第三方组件/资源</h4><p><strong>2.2.1【必须】使用可信范围内的统计组件</strong></p>
<p><strong>2.2.2 【必须】禁止引入非可信来源的第三方JS</strong></p>
<p><a id="1.2.3"></a></p>
<h4 id="2-3-纵深安全防护"><a href="#2-3-纵深安全防护" class="headerlink" title="2.3 纵深安全防护"></a>2.3 纵深安全防护</h4><p><strong>2.3.1【推荐】部署CSP，并启用严格模式</strong></p>
<p><a id="2"></a></p>
<h2 id="Node-js后台类"><a href="#Node-js后台类" class="headerlink" title="Node.js后台类"></a>Node.js后台类</h2><p><a id="2.1"></a></p>
<h3 id="I-代码实现-1"><a href="#I-代码实现-1" class="headerlink" title="I. 代码实现"></a>I. 代码实现</h3><p><a id="2.1.1"></a></p>
<h4 id="1-1-输入验证"><a href="#1-1-输入验证" class="headerlink" title="1.1 输入验证"></a>1.1 输入验证</h4><p><strong>1.1.1【必须】按类型进行数据校验</strong></p>
<ul>
<li>所有程序外部输入的参数值，应进行数据校验。校验内容包括但不限于：数据长度、数据范围、数据类型与格式。校验不通过，应拒绝。</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// bad：未进行输入验证</span></span><br><span class="line">Router.get(<span class="string">&quot;/vulxss&quot;</span>, <span class="function">(<span class="params">req, res</span>) =&gt;</span> &#123;</span><br><span class="line"> <span class="keyword">const</span> &#123; txt &#125; = req.query;</span><br><span class="line"> res.set(<span class="string">&quot;Content-Type&quot;</span>, <span class="string">&quot;text/html&quot;</span>);</span><br><span class="line"> res.send(&#123;</span><br><span class="line">  data: txt,</span><br><span class="line"> &#125;);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// good：按数据类型，进行输入验证</span></span><br><span class="line"><span class="keyword">const</span> Router = <span class="built_in">require</span>(<span class="string">&quot;express&quot;</span>).Router();</span><br><span class="line"><span class="keyword">const</span> validator = <span class="built_in">require</span>(<span class="string">&quot;validator&quot;</span>);</span><br><span class="line"></span><br><span class="line">Router.get(<span class="string">&quot;/email_with_validator&quot;</span>, <span class="function">(<span class="params">req, res</span>) =&gt;</span> &#123;</span><br><span class="line"> <span class="keyword">const</span> txt = req.query.txt || <span class="string">&quot;&quot;</span>;</span><br><span class="line"> <span class="keyword">if</span> (validator.isEmail(txt)) &#123;</span><br><span class="line">  res.send(&#123;</span><br><span class="line">   data: txt,</span><br><span class="line">  &#125;);</span><br><span class="line"> &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">  res.send(&#123; <span class="attr">err</span>: <span class="number">1</span> &#125;);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<p><em>关联漏洞：纵深防护措施 - 安全性增强特性</em></p>
<p><a id="2.1.2"></a></p>
<h4 id="1-2-执行命令"><a href="#1-2-执行命令" class="headerlink" title="1.2 执行命令"></a>1.2 执行命令</h4><p><strong>1.2.1 【必须】使用child_process执行系统命令，应限定或校验命令和参数的内容</strong></p>
<ul>
<li><p>适用场景包括：<code>child_process.exec</code>, <code>child_process.execSync</code>, <code>child_process.spawn</code>, <code>child_process.spawnSync</code>, <code>child_process.execFile</code>, <code>child_process.execFileSync</code></p>
</li>
<li><p>调用上述函数，应首先考虑限定范围，供用户选择。</p>
</li>
<li><p>使用<code>child_process.exec</code>或<code>child_process.execSync</code>时，如果可枚举输入的参数内容或者格式，则应限定白名单。如果无法枚举命令或参数，则必须过滤或者转义指定符号，包括：<code>|;&amp;$()&gt;&lt;`!</code></p>
</li>
<li><p>使用<code>child_process.spawn</code> 或<code>child_process.execFile</code>时，应校验传入的命令和参数在可控列表内。</p>
</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> Router = <span class="built_in">require</span>(<span class="string">&quot;express&quot;</span>).Router();</span><br><span class="line"><span class="keyword">const</span> validator = <span class="built_in">require</span>(<span class="string">&quot;validator&quot;</span>);</span><br><span class="line"><span class="keyword">const</span> &#123; exec &#125; = <span class="built_in">require</span>(<span class="string">&#x27;child_process&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// bad：未限定或过滤，直接执行命令</span></span><br><span class="line">Router.get(<span class="string">&quot;/vul_cmd_inject&quot;</span>, <span class="function">(<span class="params">req, res</span>) =&gt;</span> &#123;</span><br><span class="line"> <span class="keyword">const</span> txt = req.query.txt || <span class="string">&quot;echo 1&quot;</span>;</span><br><span class="line"> exec(txt, <span class="function">(<span class="params">err, stdout, stderr</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">if</span> (err) &#123; res.send(&#123; <span class="attr">err</span>: <span class="number">1</span> &#125;) &#125;</span><br><span class="line">  res.send(&#123;stdout, stderr&#125;);</span><br><span class="line"> &#125;);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// good：通过白名单，限定外部可执行命令范围</span></span><br><span class="line">Router.get(<span class="string">&quot;/not_vul_cmd_inject&quot;</span>, <span class="function">(<span class="params">req, res</span>) =&gt;</span> &#123;</span><br><span class="line"> <span class="keyword">const</span> txt = req.query.txt || <span class="string">&quot;echo 1&quot;</span>;</span><br><span class="line">  <span class="keyword">const</span> phone = req.query.phone || <span class="string">&quot;&quot;</span>;</span><br><span class="line"> <span class="keyword">const</span> cmdList = &#123;</span><br><span class="line">     sendmsg: <span class="string">&quot;./sendmsg &quot;</span></span><br><span class="line"> &#125;;</span><br><span class="line"> <span class="keyword">if</span> (txt <span class="keyword">in</span> cmdList &amp;&amp; validator.isMobilePhone(phone)) &#123;</span><br><span class="line">        exec(cmdList[txt] + phone, <span class="function">(<span class="params">err, stdout, stderr</span>) =&gt;</span> &#123;</span><br><span class="line">          <span class="keyword">if</span> (err) &#123; res.send(&#123; <span class="attr">err</span>: <span class="number">1</span> &#125;) &#125;;</span><br><span class="line">          res.send(&#123;stdout, stderr&#125;);</span><br><span class="line">        &#125;);</span><br><span class="line"> &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">  res.send(&#123;</span><br><span class="line">   err: <span class="number">1</span>,</span><br><span class="line">   tips: <span class="string">`you can use &#x27;<span class="subst">$&#123;<span class="built_in">Object</span>.keys(cmdList)&#125;</span>&#x27;`</span>,</span><br><span class="line">  &#125;);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// good：执行命令前，过滤/转义指定符号</span></span><br><span class="line">Router.get(<span class="string">&quot;/not_vul_cmd_inject&quot;</span>, <span class="function">(<span class="params">req, res</span>) =&gt;</span> &#123;</span><br><span class="line"> <span class="keyword">const</span> txt = req.query.txt || <span class="string">&quot;echo 1&quot;</span>;</span><br><span class="line">  <span class="keyword">let</span> phone = req.query.phone || <span class="string">&quot;&quot;</span>;</span><br><span class="line"> <span class="keyword">const</span> cmdList = &#123;</span><br><span class="line">     sendmsg: <span class="string">&quot;./sendmsg &quot;</span></span><br><span class="line"> &#125;;</span><br><span class="line"> phone = phone.replace(<span class="regexp">/(\||;|&amp;|\$\(|\(|\)|&gt;|&lt;|\`|!)/gi</span>,<span class="string">&quot;&quot;</span>);</span><br><span class="line"> <span class="keyword">if</span> (txt <span class="keyword">in</span> cmdList) &#123;</span><br><span class="line">        exec(cmdList[txt] + phone, <span class="function">(<span class="params">err, stdout, stderr</span>) =&gt;</span> &#123;</span><br><span class="line">          <span class="keyword">if</span> (err) &#123; res.send(&#123; <span class="attr">err</span>: <span class="number">1</span> &#125;) &#125;;</span><br><span class="line">          res.send(&#123;stdout, stderr&#125;);</span><br><span class="line">        &#125;);</span><br><span class="line"> &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">  res.send(&#123;</span><br><span class="line">   err: <span class="number">1</span>,</span><br><span class="line">   tips: <span class="string">`you can use &#x27;<span class="subst">$&#123;<span class="built_in">Object</span>.keys(cmdList)&#125;</span>&#x27;`</span>,</span><br><span class="line">  &#125;);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;);</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p><em>关联漏洞：高风险 - 任意命令执行</em></p>
<p><a id="2.1.3"></a></p>
<h4 id="1-3-文件操作"><a href="#1-3-文件操作" class="headerlink" title="1.3 文件操作"></a>1.3 文件操作</h4><p><strong>1.3.1 【必须】限定文件操作的后缀范围</strong></p>
<ul>
<li>按业务需求，使用白名单限定后缀范围。</li>
</ul>
<p><strong>1.3.2 【必须】校验并限定文件路径范围</strong></p>
<ul>
<li>应固定上传、访问文件的路径。若需要拼接外部可控变量值，检查是否包含<code>..</code>、<code>.</code>路径穿越字符。如存在，应拒绝。</li>
<li>使用<code>fs</code>模块下的函数方法时，应对第一个参数即路径部分做校验，检查是否包含路径穿越字符<code>.</code>或<code>..</code>。涉及方法包括但不限于：<code>fs.truncate</code>、<code>fs.truncateSync</code>、<code>fs.chown</code>、<code>fs.chownSync</code>、<code>fs.lchown</code>、<code>fs.lchownSync</code>、<code>fs.stat</code>、<code>fs.lchmodSync</code>、<code>fs.lstat</code>、<code>fs.statSync</code>、<code>fs.lstatSync</code>、<code>fs.readlink</code>、<code>fs.unlink</code>、<code>fs.unlinkSync</code>、<code>fs.rmdir</code>、<code>fs.rmdirSync</code>、<code>fs.mkdir</code>、<code>fs.mkdirSync</code>、<code>fs.readdir</code>、<code>fs.readdirSync</code>、<code>fs.openSync</code>、<code>fs.open</code>、<code>fs.createReadStream</code>、<code>fs.createWriteStream</code></li>
<li>使用express框架的<code>sendFile</code>方法时，应对第一个参数即路径部分做校验，检查是否包含路径穿越字符<code>.</code>或<code>..</code></li>
<li>校验时，应使用<code>path</code>模块处理前的路径参数值，或判断处理过后的路径是否穿越出了当前工作目录。涉及方法包括但不限于：<code>path.resolve</code>、<code>path.join</code>、<code>path.normalize</code>等</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> fs = <span class="built_in">require</span>(<span class="string">&quot;fs&quot;</span>);</span><br><span class="line"><span class="keyword">const</span> path = <span class="built_in">require</span>(<span class="string">&quot;path&quot;</span>);</span><br><span class="line"><span class="keyword">let</span> filename = req.query.ufile;</span><br><span class="line"><span class="keyword">let</span> root = <span class="string">&#x27;/data/ufile&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// bad：未检查文件名/路径</span></span><br><span class="line">fs.readFile(root + filename, <span class="function">(<span class="params">err, data</span>) =&gt;</span> &#123;</span><br><span class="line"> <span class="keyword">if</span> (err) &#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="built_in">console</span>.error(err);</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">`异步读取: <span class="subst">$&#123;data.toString()&#125;</span>`</span>);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// bad：使用path处理过后的路径参数值做校验，仍可能有路径穿越风险</span></span><br><span class="line">filename = path.join(root, filename);</span><br><span class="line"><span class="keyword">if</span> (filename.indexOf(<span class="string">&quot;..&quot;</span>) &lt; <span class="number">0</span>) &#123;</span><br><span class="line"> fs.readFile(filename, <span class="function">(<span class="params">err, data</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">if</span> (err) &#123;</span><br><span class="line">   <span class="keyword">return</span> <span class="built_in">console</span>.error(err);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="built_in">console</span>.log(data.toString());</span><br><span class="line"> &#125;);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// good：检查了文件名/路径，是否包含路径穿越字符</span></span><br><span class="line"><span class="keyword">if</span> (filename.indexOf(<span class="string">&quot;..&quot;</span>) &lt; <span class="number">0</span>) &#123;</span><br><span class="line"> filename = path.join(root, filename);</span><br><span class="line"> fs.readFile(filename, <span class="function">(<span class="params">err, data</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">if</span> (err) &#123;</span><br><span class="line">   <span class="keyword">return</span> <span class="built_in">console</span>.error(err);</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="built_in">console</span>.log(data.toString());</span><br><span class="line"> &#125;);</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p><strong>1.3.3 【必须】安全地处理上传文件名</strong></p>
<ul>
<li>将上传文件重命名为16位以上的随机字符串保存。</li>
<li>如需原样保留文件名，应检查是否包含<code>..</code>、<code>.</code>路径穿越字符。如存在，应拒绝。</li>
</ul>
<p><strong>1.3.4 【必须】敏感资源文件，应有加密、鉴权和水印等加固措施</strong></p>
<ul>
<li>用户上传的<code>身份证</code>、<code>银行卡</code>等图片，属敏感资源文件，应采取安全加固。</li>
<li>指向此类文件的URL，应保证不可预测性；同时，确保无接口会批量展示此类资源的URL。</li>
<li>访问敏感资源文件时，应进行权限控制。默认情况下，仅用户可查看、操作自身敏感资源文件。</li>
<li>图片类文件应添加业务水印，表明该图片仅可用于当前业务使用。</li>
</ul>
<p><a id="2.1.4"></a></p>
<h4 id="1-4-网络请求"><a href="#1-4-网络请求" class="headerlink" title="1.4 网络请求"></a>1.4 网络请求</h4><p><strong>1.4.1 【必须】限定访问网络资源地址范围</strong></p>
<ul>
<li><p>应固定程序访问网络资源地址的<code>协议</code>、<code>域名</code>、<code>路径</code>范围。</p>
</li>
<li><p>若业务需要，外部可指定访问网络资源地址，应禁止访问内网私有地址段及域名。</p>
</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;&#x2F; 以RFC定义的专有网络为例，如有自定义私有网段亦应加入禁止访问列表。</span><br><span class="line">10.0.0.0&#x2F;8</span><br><span class="line">172.16.0.0&#x2F;12</span><br><span class="line">192.168.0.0&#x2F;16</span><br><span class="line">127.0.0.0&#x2F;8</span><br></pre></td></tr></table></figure>

<p><strong>1.4.2 【推荐】请求网络资源，应加密传输</strong></p>
<ul>
<li>应优先选用https协议请求网络资源</li>
</ul>
<p><em>关联漏洞：高风险 - SSRF，高风险 - HTTP劫持</em></p>
<p><a id="2.1.5"></a></p>
<h4 id="1-5-数据输出"><a href="#1-5-数据输出" class="headerlink" title="1.5 数据输出"></a>1.5 数据输出</h4><p><strong>1.5.1 【必须】高敏感信息禁止存储、展示</strong></p>
<ul>
<li>口令、密保答案、生理标识等鉴权信息禁止展示</li>
<li>非金融类业务，信用卡cvv码及日志禁止存储</li>
</ul>
<p><strong>1.5.2【必须】一般敏感信息脱敏展示</strong></p>
<ul>
<li>身份证只显示第一位和最后一位字符，如：<code>3*********************1</code></li>
<li>移动电话号码隐藏中间6位字符，如：<code>134***************48</code></li>
<li>工作地址/家庭地址最多显示到<code>区</code>一级</li>
<li>银行卡号仅显示最后4位字符，如：<code>*********************8639</code></li>
</ul>
<p><strong>1.5.3 【推荐】返回的字段按业务需要输出</strong></p>
<ul>
<li>按需输出，避免不必要的用户信息泄露</li>
<li>用户敏感数据应在服务器后台处理后输出，不可以先输出到客户端，再通过客户端代码来处理展示</li>
</ul>
<p><em>关联漏洞：高风险 - 用户敏感信息泄露</em></p>
<p><a id="2.1.6"></a></p>
<h4 id="1-6-响应输出"><a href="#1-6-响应输出" class="headerlink" title="1.6 响应输出"></a>1.6 响应输出</h4><p><strong>1.6.1 【必须】设置正确的HTTP响应包类型</strong></p>
<ul>
<li>响应头Content-Type与实际响应内容，应保持一致。如：API响应数据类型是json，则响应头使用<code>application/json</code>；若为xml，则设置为<code>text/xml</code>。</li>
</ul>
<p><strong>1.6.2 【必须】添加安全响应头</strong></p>
<ul>
<li>所有接口、页面，添加响应头 <code>X-Content-Type-Options: nosniff</code>。</li>
<li>所有接口、页面，添加响应头<code>X-Frame-Options</code>。按需合理设置其允许范围，包括：<code>DENY</code>、<code>SAMEORIGIN</code>、<code>ALLOW-FROM origin</code>。用法参考：<a target="_blank" rel="noopener" href="https://developer.mozilla.org/zh-CN/docs/Web/HTTP/X-Frame-Options">MDN文档</a></li>
<li>推荐使用组件： <a target="_blank" rel="noopener" href="https://www.npmjs.com/package/helmet">helmet</a></li>
</ul>
<p><strong>1.6.3 【必须】外部输入拼接到响应页面前，进行编码处理</strong></p>
<table>
<thead>
<tr>
<th>场景</th>
<th>编码规则</th>
</tr>
</thead>
<tbody><tr>
<td>输出点在HTML标签之间</td>
<td>需要对以下6个特殊字符进行HTML实体编码(&amp;, &lt;, &gt;, “, ‘,/)。<br>示例：<br>&amp; –&gt; &amp;amp;<br>&lt; –&gt; &amp;lt;<br>&gt;–&gt; &amp;gt;<br>“ –&gt; &amp;quot;<br>‘ –&gt; &amp;#x27;  <br>/ –&gt; &amp;#x2F;</td>
</tr>
<tr>
<td>输出点在HTML标签普通属性内（如href、src、style等，on事件除外）</td>
<td>要对数据进行HTML属性编码。<br>编码规则：除了阿拉伯数字和字母，对其他所有的字符进行编码，只要该字符的ASCII码小于256。编码后输出的格式为&#xHH;(以&amp;#x开头，HH则是指该字符对应的十六进制数字，分号作为结束符)</td>
</tr>
<tr>
<td>输出点在JS内的数据中</td>
<td>需要进行js编码<br>编码规则：<br>除了阿拉伯数字和字母，对其他所有的字符进行编码，只要该字符的ASCII码小于256。编码后输出的格式为 \xHH （以 \x 开头，HH则是指该字符对应的十六进制数字）<br>Tips：这种场景仅限于外部数据拼接在js里被引号括起来的变量值中。除此之外禁止直接将代码拼接在js代码中。</td>
</tr>
<tr>
<td>输出点在CSS中（Style属性）</td>
<td>需要进行CSS编码<br>编码规则：<br>除了阿拉伯数字和字母，对其他所有的字符进行编码，只要该字符的ASCII码小于256。编码后输出的格式为 \HH （以 \ 开头，HH则是指该字符对应的十六进制数字）</td>
</tr>
<tr>
<td>输出点在URL属性中</td>
<td>对这些数据进行URL编码<br>Tips：除此之外，所有链接类属性应该校验其协议。禁止JavaScript、data和Vb伪协议。</td>
</tr>
</tbody></table>
<p><strong>1.6.4 【必须】响应禁止展示物理资源、程序内部代码逻辑等敏感信息</strong></p>
<ul>
<li>业务生产（正式）环境，应用异常时，响应内容禁止展示敏感信息。包括但不限于：<code>物理路径</code>、<code>程序内部源代码</code>、<code>调试日志</code>、<code>内部账号名</code>、<code>内网ip地址</code>等。</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;&#x2F; bad</span><br><span class="line">Access denied for user &#39;xxx&#39;@&#39;xx.xxx.xxx.162&#39; (using password: NO)&quot;</span><br></pre></td></tr></table></figure>

<p><strong>1.6.5 【推荐】添加安全纵深防御措施</strong></p>
<ul>
<li>部署CSP，规则中应引入最新的严格模式特性<code>nonce-</code></li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// good：使用helmet组件安全地配置响应头</span></span><br><span class="line"><span class="keyword">const</span> express = <span class="built_in">require</span>(<span class="string">&quot;express&quot;</span>);</span><br><span class="line"><span class="keyword">const</span> helmet = <span class="built_in">require</span>(<span class="string">&quot;helmet&quot;</span>);</span><br><span class="line"><span class="keyword">const</span> app = express();</span><br><span class="line">app.use(helmet());</span><br><span class="line"></span><br><span class="line"><span class="comment">// good：正确配置Content-Type、添加了安全响应头，引入了CSP</span></span><br><span class="line">Router.get(<span class="string">&quot;/&quot;</span>, <span class="function">(<span class="params">req, res</span>) =&gt;</span> &#123;</span><br><span class="line"> res.header(<span class="string">&quot;Content-Type&quot;</span>, <span class="string">&quot;application/json&quot;</span>);</span><br><span class="line"> res.header(<span class="string">&quot;X-Content-Type-Options&quot;</span>, <span class="string">&quot;nosniff&quot;</span>);</span><br><span class="line"> res.header(<span class="string">&quot;X-Frame-Options&quot;</span>, <span class="string">&quot;SAMEORIGIN&quot;</span>);</span><br><span class="line"> res.header(<span class="string">&quot;Content-Security-Policy&quot;</span>, <span class="string">&quot;script-src &#x27;self&#x27;&quot;</span>);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<p><em>关联漏洞：中风险 - XSS、中风险 - 跳转漏洞</em></p>
<p><a id="2.1.7"></a></p>
<h4 id="1-7-执行代码"><a href="#1-7-执行代码" class="headerlink" title="1.7 执行代码"></a>1.7 执行代码</h4><p><strong>1.7.1 【必须】安全的代码执行方式</strong></p>
<ul>
<li>禁止使用 <code>eval</code> 函数</li>
<li>禁止使用<code>new Function(&quot;input&quot;)()</code> 来创建函数</li>
<li>使用 <code>setInteval</code>，<code>setTimeout</code>，应校验传入的参数</li>
</ul>
<p><em>关联漏洞：高风险 - 代码执行漏洞</em></p>
<p><a id="2.1.8"></a></p>
<h4 id="1-8-Web跨域"><a href="#1-8-Web跨域" class="headerlink" title="1.8 Web跨域"></a>1.8 Web跨域</h4><p><strong>1.8.1 【必须】限定JSONP接口的callback字符集范围</strong></p>
<ul>
<li>JSONP接口的callback函数名为固定白名单。如callback函数名可用户自定义，应限制函数名仅包含 字母、数字和下划线。如：<code>[a-zA-Z0-9_-]+</code></li>
</ul>
<p><strong>1.8.2 【必须】安全的CORS配置</strong></p>
<ul>
<li>使用CORS，应对请求头Origin值做严格过滤、校验。具体来说，可以使用“全等于”判断，或使用严格的正则进行判断。如：<code>^https://domain\.qq\.com$</code></li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// good：使用全等于，校验请求的Origin</span></span><br><span class="line"><span class="keyword">if</span> (req.headers.origin === <span class="string">&#x27;https://domain.qq.com&#x27;</span>) &#123;</span><br><span class="line">    res.setHeader(<span class="string">&#x27;Access-Control-Allow-Origin&#x27;</span>, req.headers.origin);</span><br><span class="line">    res.setHeader(<span class="string">&#x27;Access-Control-Allow-Credentials&#x27;</span>, <span class="literal">true</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><em>关联漏洞：中风险 - XSS，中风险 - CSRF，中风险 - CORS配置不当</em></p>
<p><a id="2.1.9"></a></p>
<h4 id="1-9-SQL操作"><a href="#1-9-SQL操作" class="headerlink" title="1.9 SQL操作"></a>1.9 SQL操作</h4><p><strong>1.9.1 【必须】SQL语句默认使用预编译并绑定变量</strong></p>
<ul>
<li><p>应使用预编译绑定变量的形式编写sql语句，保持查询语句和数据相分离</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// bad：拼接SQL语句查询，存在安全风险</span></span><br><span class="line"><span class="keyword">const</span> mysql  = <span class="built_in">require</span>(<span class="string">&quot;mysql&quot;</span>);</span><br><span class="line"><span class="keyword">const</span> connection = mysql.createConnection(options);</span><br><span class="line">connection.connect();</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> sql = util.format(<span class="string">&quot;SELECT * from some_table WHERE Id = %s and Name = %s&quot;</span>, req.body.id, req.body.name);</span><br><span class="line">connection.query(sql, <span class="function">(<span class="params">err, result</span>) =&gt;</span> &#123;</span><br><span class="line"> <span class="comment">// handle err..</span></span><br><span class="line">&#125;);  </span><br><span class="line"></span><br><span class="line"><span class="comment">// good：使用预编译绑定变量构造SQL语句</span></span><br><span class="line"><span class="keyword">const</span> mysql  = <span class="built_in">require</span>(<span class="string">&quot;mysql&quot;</span>);</span><br><span class="line"><span class="keyword">const</span> connection = mysql.createConnection(options);</span><br><span class="line">connection.connect();</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> sql = <span class="string">&quot;SELECT * from some_table WHERE Id = ? and Name = ?&quot;</span>;</span><br><span class="line"><span class="keyword">const</span> sqlParams = [req.body.id, req.body.name];</span><br><span class="line">connection.query(sql, sqlParams, <span class="function">(<span class="params">err, result</span>) =&gt;</span> &#123;</span><br><span class="line"> <span class="comment">// handle err..</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
</li>
<li><p>对于表名、列名等无法进行预编译的场景，如：<code>__user_input__</code> 拼接到比如 <code>limit</code>, <code>order by</code>, <code>group by</code> , <code>from tablename</code>语句中。请使用以下方法：</p>
<p><em>方案1：使用白名单校验表名/列名</em></p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// good</span></span><br><span class="line"><span class="keyword">const</span> tableSuffix = req.body.type;</span><br><span class="line"><span class="keyword">if</span> ([<span class="string">&quot;expected1&quot;</span>, <span class="string">&quot;expected2&quot;</span>].indexOf(tableSuffix) &lt; <span class="number">0</span>) &#123;</span><br><span class="line"> <span class="comment">// 不在表名白名单中，拒绝请求</span></span><br><span class="line"> <span class="keyword">return</span> ;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">const</span> sql = <span class="string">`SELECT * from t_business_<span class="subst">$&#123;tableSuffix&#125;</span>`</span>;</span><br><span class="line">connection.query(sql, <span class="function">(<span class="params">err, result</span>) =&gt;</span> &#123;</span><br><span class="line"> <span class="comment">// handle err..</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<p><em>方案2：使用反引号包裹表名/列名，并过滤 <code>__user_input__</code> 中的反引号</em></p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// good</span></span><br><span class="line"><span class="keyword">let</span> &#123; orderType &#125; = req.body;</span><br><span class="line"><span class="comment">// 过滤掉__user_input__中的反引号</span></span><br><span class="line">orderType = orderType.replace(<span class="string">&quot;`&quot;</span>, <span class="string">&quot;&quot;</span>);</span><br><span class="line"><span class="keyword">const</span> sql = util.format(<span class="string">&quot;SELECT * from t_business_feeds order by `%s`&quot;</span>, orderType);</span><br><span class="line">connection.query(sql, <span class="function">(<span class="params">err, result</span>) =&gt;</span> &#123;</span><br><span class="line"> <span class="comment">// handle err..</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<p><em>方案3：将 <code>__user_input__</code> 转换为整数</em></p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// good</span></span><br><span class="line"><span class="keyword">let</span> &#123; orderType &#125; = req.body;</span><br><span class="line"><span class="comment">// 强制转换为整数</span></span><br><span class="line">orderType = <span class="built_in">parseInt</span>(orderType, <span class="number">10</span>);</span><br><span class="line"><span class="keyword">const</span> sql = <span class="string">`SELECT * from t_business_feeds order by <span class="subst">$&#123;orderType&#125;</span>`</span>;</span><br><span class="line">connection.query(sql, <span class="function">(<span class="params">err, result</span>) =&gt;</span> &#123;</span><br><span class="line"> <span class="comment">// handle err..</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

</li>
</ul>
<p><strong>1.9.2 【必须】安全的ORM操作</strong></p>
<ul>
<li><p>使用安全的ORM组件进行数据库操作。如 <code>sequelize</code> 等</p>
</li>
<li><p>禁止<code>__user_input__</code>以拼接的方式直接传入ORM的各类raw方法</p>
</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//bad: adonisjs ORM</span></span><br><span class="line"><span class="comment">//参考：https://adonisjs.com/docs/3.2/security-introduction#_sql_injection</span></span><br><span class="line"><span class="keyword">const</span> username = request.param(<span class="string">&quot;username&quot;</span>);</span><br><span class="line"><span class="keyword">const</span> users = <span class="keyword">yield</span> Database</span><br><span class="line">  .table(<span class="string">&quot;users&quot;</span>)</span><br><span class="line">  .where(Database.raw(<span class="string">`username = <span class="subst">$&#123;username&#125;</span>`</span>));</span><br><span class="line"></span><br><span class="line"><span class="comment">//good: adonisjs ORM</span></span><br><span class="line"><span class="keyword">const</span> username = request.param(<span class="string">&quot;username&quot;</span>);</span><br><span class="line"><span class="keyword">const</span> users = <span class="keyword">yield</span> Database</span><br><span class="line">  .table(<span class="string">&#x27;users&#x27;</span>)</span><br><span class="line">  .where(Database.raw(<span class="string">&quot;username = ?&quot;</span>, [username]));</span><br></pre></td></tr></table></figure>

<ul>
<li>使用ORM进行Update/Insert操作时，应限制操作字段范围</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">good</span></span><br><span class="line"><span class="comment">假设该api用于插入用户的基本信息，使用传入的req.body通过Sequelize的create方法实现</span></span><br><span class="line"><span class="comment">假设User包含字段：username,email,isAdmin，</span></span><br><span class="line"><span class="comment">其中,isAdmin将会用于是否系统管理员的鉴权，默认值为false</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="comment">// Sequelize: 只允许变更username、email字段值</span></span><br><span class="line">User.create(req.body, &#123; <span class="attr">fields</span>: [<span class="string">&quot;username&quot;</span>, <span class="string">&quot;email&quot;</span>] &#125;).then(<span class="function">(<span class="params">user</span>) =&gt;</span> &#123;</span><br><span class="line"> <span class="comment">// handle the rest..</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<blockquote>
<p><strong>为什么要这么做？</strong><br>在上述案例中，若不限定fields值，攻击者将可传入<code>&#123;&quot;username&quot;:&quot;boo&quot;,&quot;email&quot;:&quot;foo@boo.com&quot;,&quot;isAdmin&quot;:true&#125;</code>将自己变为<code>Admin</code>，产生垂直越权漏洞。</p>
</blockquote>
<p><em>关联漏洞：高风险 - SQL注入，中风险 - Mass Assignment 逻辑漏洞</em></p>
<p><a id="2.1.10"></a></p>
<h4 id="1-10-NoSQL操作"><a href="#1-10-NoSQL操作" class="headerlink" title="1.10 NoSQL操作"></a>1.10 NoSQL操作</h4><p><strong>1.10.1 【必须】校验参数值类型</strong></p>
<ul>
<li>将HTTP参数值代入NoSQL操作前，应校验类型。如非功能需要，禁止对象（Object）类型传入。</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// bad：执行NOSQL操作前，未作任何判断</span></span><br><span class="line">app.post(<span class="string">&quot;/&quot;</span>, <span class="function">(<span class="params">req, res</span>) =&gt;</span> &#123;</span><br><span class="line"> db.users.find(&#123; <span class="attr">username</span>: req.body.username, <span class="attr">password</span>: req.body.password &#125;, <span class="function">(<span class="params">err, users</span>) =&gt;</span> &#123;</span><br><span class="line"> <span class="comment">// **<span class="doctag">TODO:</span>** handle the rest</span></span><br><span class="line"> &#125;);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// good：在进入nosql前先判断`__USER_INPUT__`是否为字符串。</span></span><br><span class="line">app.post(<span class="string">&quot;/&quot;</span>, <span class="function">(<span class="params">req, res</span>) =&gt;</span> &#123;</span><br><span class="line"> <span class="keyword">if</span> (req.body.username &amp;&amp; <span class="keyword">typeof</span> req.body.username !== <span class="string">&quot;string&quot;</span>) &#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">&quot;username must be a string&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">if</span> (req.body.password &amp;&amp; <span class="keyword">typeof</span> req.body.password !== <span class="string">&quot;string&quot;</span>) &#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">&quot;password must be a string&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line"> db.users.find(&#123; <span class="attr">username</span>: req.body.username, <span class="attr">password</span>: req.body.password &#125;, <span class="function">(<span class="params">err, users</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="comment">// **<span class="doctag">TODO:</span>** handle the rest</span></span><br><span class="line"> &#125;);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<blockquote>
<p><strong>为什么要这么做？</strong></p>
<p>JavaScript中，从http或socket接收的数据可能不是单纯的字符串，而是被黑客精心构造的对象(Object)。在本例中：</p>
<ul>
<li>期望接收的POST数据：<code>username=foo&amp;password=bar</code></li>
<li>期望的等价条件查询sql语句：<code>select * from users where username = &#39;foo&#39; and password = &#39;bar&#39;</code></li>
<li>黑客的精心构造的攻击POST数据：<code>username[$ne]=null&amp;password[$ne]=null</code>或JSON格式：<code>&#123;&quot;username&quot;: &#123;&quot;$ne&quot;: null&#125;,&quot;password&quot;: &#123;&quot;$ne&quot;: null&#125;&#125;</code></li>
<li>黑客篡改后的等价条件查询sql语句：<code>select * from users where username != null &amp; password != null</code></li>
<li>黑客攻击结果：绕过正常逻辑，在不知道他人的username/password的情况登录他人账号。</li>
</ul>
</blockquote>
<p><strong>1.10.2 【必须】NoSQL操作前，应校验权限/角色</strong></p>
<ul>
<li>执行NoSQL增、删、改、查逻辑前，应校验权限</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用express、mongodb(mongoose)实现的删除文章demo</span></span><br><span class="line"><span class="comment">// bad：在删除文章前未做权限校验</span></span><br><span class="line">app.post(<span class="string">&quot;/deleteArticle&quot;</span>, <span class="function">(<span class="params">req, res</span>) =&gt;</span> &#123;</span><br><span class="line"> db.articles.deleteOne(&#123; <span class="attr">article_id</span>: req.body.article_id &#125;, <span class="function">(<span class="params">err, users</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="comment">// <span class="doctag">TODO:</span> handle the rest</span></span><br><span class="line"> &#125;);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// good：进入nosql语句前先进行权限校验</span></span><br><span class="line">app.post(<span class="string">&quot;/deleteArticle&quot;</span>, <span class="function">(<span class="params">req, res</span>) =&gt;</span> &#123;</span><br><span class="line"> checkPriviledge(ctx.uin, req.body.article_id);</span><br><span class="line"> db.articles.deleteOne(&#123; <span class="attr">article_id</span>: req.body.article_id &#125;, <span class="function">(<span class="params">err, users</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="comment">// <span class="doctag">TODO:</span> handle the rest</span></span><br><span class="line"> &#125;);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<p><em>关联漏洞：高风险 - 越权操作，高风险 - NoSQL注入</em></p>
<p><a id="2.1.11"></a></p>
<h4 id="1-11-服务器端渲染（SSR）"><a href="#1-11-服务器端渲染（SSR）" class="headerlink" title="1.11 服务器端渲染（SSR）"></a>1.11 服务器端渲染（SSR）</h4><p><strong>1.11.1 【必须】安全的Vue服务器端渲染(Vue SSR)</strong></p>
<ul>
<li><p>禁止直接将不受信的外部内容传入<code>&#123;&#123;&#123; data &#125;&#125;&#125;</code>表达式中</p>
</li>
<li><p>模板内容禁止被污染</p>
</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// bad: 将用户输入替换进模板</span></span><br><span class="line"><span class="keyword">const</span> app = <span class="keyword">new</span> Vue(&#123;</span><br><span class="line"> template: appTemplate.replace(<span class="string">&quot;word&quot;</span>, __USER_INPUT__),</span><br><span class="line">&#125;);</span><br><span class="line">renderer.renderToString(app);</span><br></pre></td></tr></table></figure>

<ul>
<li>对已渲染的HTML文本内容（renderToString后的html内容）。如需再拼不受信的外部输入，应先进行安全过滤，具体请参考<strong>1.6.3</strong></li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// bad: 渲染后的html再拼接不受信的外部输入</span></span><br><span class="line"><span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>((<span class="function">(<span class="params">resolve</span>) =&gt;</span> &#123;</span><br><span class="line"> renderer.renderToString(component, <span class="function">(<span class="params">err, html</span>) =&gt;</span> &#123;</span><br><span class="line">  <span class="keyword">let</span> htmlOutput = html;</span><br><span class="line">  htmlOutput += <span class="string">`<span class="subst">$&#123;__USER_INPUT__&#125;</span>`</span>;</span><br><span class="line">  resolve(htmlOutput);</span><br><span class="line"> &#125;);</span><br><span class="line">&#125;));</span><br></pre></td></tr></table></figure>

<p><strong>1.11.2 【必须】安全地使用EJS、LoDash、UnderScore进行服务器端渲染</strong></p>
<ul>
<li><p>使用render函数时，模板内容禁止被污染</p>
<p>lodash.Template:</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// bad: 将用户输入送进模板</span></span><br><span class="line"><span class="keyword">const</span> compiled = _.template(<span class="string">`&lt;b&gt;<span class="subst">$&#123;__USER_INPUT__&#125;</span>&lt;%- value %&gt;&lt;/b&gt;`</span>);</span><br><span class="line">compiled(&#123; <span class="attr">value</span>: <span class="string">&quot;hello&quot;</span> &#125;);</span><br></pre></td></tr></table></figure>

<p>ejs:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// bad: 将用户输入送进模板</span></span><br><span class="line"><span class="keyword">const</span> ejs = <span class="built_in">require</span>(<span class="string">&quot;ejs&quot;</span>);</span><br><span class="line"><span class="keyword">const</span> people = [<span class="string">&quot;geddy&quot;</span>, <span class="string">&quot;neil&quot;</span>, <span class="string">&quot;alex&quot;</span>];</span><br><span class="line"><span class="keyword">const</span> html = ejs.render(<span class="string">`&lt;%= people.join(&quot;, &quot;); %&gt;<span class="subst">$&#123;__USER_INPUT__&#125;</span>`</span>, &#123; people &#125;);</span><br></pre></td></tr></table></figure>
</li>
<li><p>Ejs、LoDash、UnderScore提供的HTML插值模板默认形似<code>&lt;%= data %&gt;</code>，尽管在默认情况下<code>&lt;%= data %&gt;</code>存在过滤，在编写HTML插值模板时需注意:</p>
<ol>
<li>用户输入流入html属性值时，必须使用双引号包裹：<code>&lt;base data-id = &quot;&lt;%= __USER_INPUT__ %&gt;&quot;&gt;</code></li>
<li>用户输入流入<code>&lt;script&gt;&lt;/script&gt;</code>标签或on*的html属性中时，如<code>&lt;script&gt;var id = &lt;%= __USER_INPUT__ %&gt;&lt;/script&gt;</code> ，须按照1.6.3中的做法或白名单方法进行过滤，框架/组件的过滤在此处不起作用</li>
</ol>
</li>
</ul>
<p><strong>1.11.3 【必须】在自行实现状态存储容器并将其JSON.Stringify序列化后注入到HTML时，必须进行安全过滤</strong></p>
<p><a id="2.1.12"></a></p>
<h4 id="1-12-URL跳转"><a href="#1-12-URL跳转" class="headerlink" title="1.12 URL跳转"></a>1.12 URL跳转</h4><p><strong>1.12.1【必须】限定跳转目标地址</strong></p>
<ul>
<li>适用场景包括：</li>
</ul>
<ol>
<li>使用30x返回码并在Header中设置Location进行跳转</li>
<li>在返回页面中打印<code>&lt;script&gt;location.href=__Redirection_URL__&lt;/script&gt;</code></li>
</ol>
<ul>
<li>使用白名单，限定重定向地址的协议前缀（默认只允许HTTP、HTTPS）、域名（默认只允许公司根域），或指定为固定值；</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用express实现的登录成功后的回调跳转页面</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// bad: 未校验页面重定向地址</span></span><br><span class="line">app.get(<span class="string">&quot;/login&quot;</span>, <span class="function">(<span class="params">req, res</span>) =&gt;</span> &#123;</span><br><span class="line"> <span class="comment">// 若未登录用户访问其他页面，则让用户导向到该处理函数进行登录</span></span><br><span class="line">  <span class="comment">// 使用参数loginCallbackUrl记录先前尝试访问的url，在登录成功后跳转回loginCallbackUrl:</span></span><br><span class="line"> <span class="keyword">const</span> &#123; loginCallbackUrl &#125; = req.query;</span><br><span class="line"> <span class="keyword">if</span> (loginCallbackUrl) &#123;</span><br><span class="line">     res.redirect(loginCallbackUrl);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// good: 白名单限定重定向地址</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">isValidURL</span>(<span class="params">sUrl</span>) </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> !!((<span class="regexp">/^(https?:\/\/)?[\w\-.]+\.(qq|tencent)\.com($|\/|\\)/i</span>).test(sUrl) || (<span class="regexp">/^[\w][\w/.\-_%]+$/i</span>).test(sUrl) || (<span class="regexp">/^[/\\][^/\\]/i</span>).test(sUrl));</span><br><span class="line">&#125;</span><br><span class="line">app.get(<span class="string">&quot;/login&quot;</span>, <span class="function">(<span class="params">req, res</span>) =&gt;</span> &#123;</span><br><span class="line"> <span class="comment">// 若未登录用户访问其他页面，则让用户导向到该处理函数进行登录</span></span><br><span class="line">  <span class="comment">// 使用参数loginCallbackUrl记录先前尝试访问的url，在登录成功后跳转回loginCallbackUrl:</span></span><br><span class="line"> <span class="keyword">const</span> &#123; loginCallbackUrl &#125; = req.query;</span><br><span class="line"> <span class="keyword">if</span> (loginCallbackUrl &amp;&amp; isValidUrl(loginCallbackUrl)) &#123;</span><br><span class="line">     res.redirect(loginCallbackUrl);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// good: 白名单限定重定向地址，通过返回html实现</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">isValidURL</span>(<span class="params">sUrl</span>) </span>&#123;</span><br><span class="line"> <span class="keyword">return</span> !!((<span class="regexp">/^(https?:\/\/)?[\w\-.]+\.(qq|tencent)\.com($|\/|\\)/i</span>).test(sUrl) || (<span class="regexp">/^[\w][\w/.\-_%]+$/i</span>).test(sUrl) || (<span class="regexp">/^[/\\][^/\\]/i</span>).test(sUrl));</span><br><span class="line">&#125;</span><br><span class="line">app.get(<span class="string">&quot;/login&quot;</span>, <span class="function">(<span class="params">req, res</span>) =&gt;</span> &#123;</span><br><span class="line"> <span class="comment">// 若未登录用户访问其他页面，则让用户导向到该处理函数进行登录</span></span><br><span class="line">  <span class="comment">// 使用参数loginCallbackUrl记录先前尝试访问的url，在登录成功后跳转回loginCallbackUrl:</span></span><br><span class="line"> <span class="keyword">const</span> &#123; loginCallbackUrl &#125; = req.query;</span><br><span class="line"> <span class="keyword">if</span> (loginCallbackUrl &amp;&amp; isValidUrl(loginCallbackUrl)) &#123;</span><br><span class="line">  <span class="comment">// 使用encodeURI，过滤左右尖括号与双引号，防止逃逸出包裹的双引号</span></span><br><span class="line">  <span class="keyword">const</span> redirectHtml = <span class="string">`&lt;script&gt;location.href = &quot;<span class="subst">$&#123;<span class="built_in">encodeURI</span>(loginCallbackUrl)&#125;</span>&quot;;&lt;/script&gt;`</span>;</span><br><span class="line">     res.end(redirectHtml);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<p><em>关联漏洞：中风险 - 任意URL跳转漏洞</em></p>
<p><a id="2.1.13"></a></p>
<h4 id="1-13-Cookie与登录态"><a href="#1-13-Cookie与登录态" class="headerlink" title="1.13 Cookie与登录态"></a>1.13 Cookie与登录态</h4><p><strong>1.13.1【推荐】为Cookies中存储的关键登录态信息添加http-only保护</strong></p>
<p><em>关联漏洞：纵深防护措施 - 安全性增强特性</em></p>
<p><a id="2.2"></a></p>
<h3 id="II-配置-amp-环境-1"><a href="#II-配置-amp-环境-1" class="headerlink" title="II. 配置&amp;环境"></a>II. 配置&amp;环境</h3><p><a id="2.2.1"></a></p>
<h4 id="2-1-依赖库"><a href="#2-1-依赖库" class="headerlink" title="2.1 依赖库"></a>2.1 依赖库</h4><p><strong>2.1.1【必须】使用安全的依赖库</strong></p>
<ul>
<li>使用自动工具，检查依赖库是否存在后门/漏洞，保持最新版本</li>
</ul>
<p><a id="2.2.2"></a></p>
<h4 id="2-2-运行环境"><a href="#2-2-运行环境" class="headerlink" title="2.2 运行环境"></a>2.2 运行环境</h4><p><strong>2.2.1 【必须】使用非root用户运行Node.js</strong></p>
<p><a id="2.2.3"></a></p>
<h4 id="2-3-配置信息"><a href="#2-3-配置信息" class="headerlink" title="2.3 配置信息"></a>2.3 配置信息</h4><p><strong>2.3.1【必须】禁止硬编码认证凭证</strong></p>
<ul>
<li>禁止在源码中硬编码<code>AK/SK</code>、<code>数据库账密</code>、<code>私钥证书</code>等配置信息</li>
<li>应使用配置系统或KMS密钥管理系统。</li>
</ul>
<p><strong>2.3.2【必须】禁止硬编码IP配置</strong></p>
<ul>
<li>禁止在源码中硬编码<code>IP</code>信息</li>
</ul>
<blockquote>
<p><strong>为什么要这么做？</strong></p>
<p>硬编码IP可能会导致后续机器裁撤或变更时产生额外的工作量，影响系统的可靠性。</p>
</blockquote>
<p><strong>2.3.3【必须】禁止硬编码员工敏感信息</strong></p>
<ul>
<li>禁止在源代码中含员工敏感信息，包括但不限于：<code>员工ID</code>、<code>手机号</code>、<code>微信/QQ号</code>等。</li>
</ul>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kity@2.0.4/dist/kity.min.js"></script><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kityminder-core@1.4.50/dist/kityminder.core.min.js"></script><script defer="true" type="text/javascript" src="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.js"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.css">
      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="https://gz1234.gitee.io/2020/10/29/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E9%A2%98/node%E9%9D%A2%E8%AF%95%E9%A2%98/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="郭泽">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="郭泽">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | 郭泽">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2020/10/29/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E9%A2%98/node%E9%9D%A2%E8%AF%95%E9%A2%98/" class="post-title-link" itemprop="url">node面试题</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2020-10-29 11:09:28" itemprop="dateCreated datePublished" datetime="2020-10-29T11:09:28+08:00">2020-10-29</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2022-10-31 11:06:22" itemprop="dateModified" datetime="2022-10-31T11:06:22+08:00">2022-10-31</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E9%A2%98/" itemprop="url" rel="index"><span itemprop="name">前端面试题</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h2 id="npm-run-xxx-的过程"><a href="#npm-run-xxx-的过程" class="headerlink" title="npm run xxx 的过程"></a>npm run xxx 的过程</h2><ul>
<li>运行 npm run XXX</li>
<li>npm 先在当前目录 的 node_modules/.bin 查找要执行的程序，如果找到则运行；没有找到 则从全局的 node_modules/.bin 中查找，npm i -g xxx 就是安装到到全局目录；</li>
<li>如果全局目录还是没找到，那么就从 path 环境变量中查找有没有其他同名的可执行程序。</li>
<li><a target="_blank" rel="noopener" href="https://blog.csdn.net/qq_31967985/article/details/124367380?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_title~default-1-124367380-blog-125222803.pc_relevant_multi_platform_whitelistv2_exp180w&spm=1001.2101.3001.4242.2&utm_relevant_index=4">npm install 到npm run xxx深度解读</a></li>
</ul>
          <!--noindex-->
            <div class="post-button">
              <a class="btn" href="/2020/10/29/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E9%A2%98/node%E9%9D%A2%E8%AF%95%E9%A2%98/#more" rel="contents">
                阅读全文 &raquo;
              </a>
            </div>
          <!--/noindex-->
        
      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="https://gz1234.gitee.io/2020/10/29/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E9%A2%98/vue%E9%9D%A2%E8%AF%95%E9%A2%98/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="郭泽">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="郭泽">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | 郭泽">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2020/10/29/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E9%A2%98/vue%E9%9D%A2%E8%AF%95%E9%A2%98/" class="post-title-link" itemprop="url">vue面试题</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2020-10-29 11:09:28" itemprop="dateCreated datePublished" datetime="2020-10-29T11:09:28+08:00">2020-10-29</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2023-05-16 18:39:16" itemprop="dateModified" datetime="2023-05-16T18:39:16+08:00">2023-05-16</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E9%A2%98/" itemprop="url" rel="index"><span itemprop="name">前端面试题</span></a>
        </span>
          ，
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/vue/" itemprop="url" rel="index"><span itemprop="name">vue</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h2 id="vue-mixin"><a href="#vue-mixin" class="headerlink" title="vue mixin"></a>vue mixin</h2><ul>
<li>同名钩子函数将合并为一个数组，因此都将被调用。另外，混入对象的钩子将在组件自身钩子之前调用。</li>
<li>数据对象在内部会进行递归合并，并在发生冲突时以组件数据优先</li>
<li>值为对象的选项，例如 methods、components，将被合并为同一个对象。两个对象键名冲突时，取组件对象的键值对</li>
</ul>
<h2 id="路由钩子函数有三种"><a href="#路由钩子函数有三种" class="headerlink" title="路由钩子函数有三种"></a>路由钩子函数有三种</h2><ul>
<li>全局钩子： beforeEach、 afterEach</li>
<li>单个路由里面的钩子： beforeEnter、 beforeLeave</li>
<li>组件钩子：beforeRouteEnter、 beforeRouteUpdate、 beforeRouteLeave</li>
</ul>
<h2 id="react-vue-中的-key-的作用-内部原理是什么"><a href="#react-vue-中的-key-的作用-内部原理是什么" class="headerlink" title="react/vue 中的 key 的作用/内部原理是什么"></a>react/vue 中的 key 的作用/内部原理是什么</h2><ol>
<li>虚拟 DOM 的 key 的作用?<ol>
<li>简单说: key 是虚拟 DOM 对象的标识, 在更新显示时 key 起着极其重要的作用</li>
<li>详细说: 当列表数组中的数据发生变化生成新的虚拟 DOM 后, React 进行新旧虚拟 DOM 的 diff 比较<ul>
<li>key 没有变<br> item 数据没变, 直接使用原来的真实 DOM<br> item 数据变了, 对原来的真实 DOM 进行数据更新</li>
<li>key 变了销毁原来的真实 DOM, 根据 item 数据创建新的真实 DOM 显示(即使 item 数据没有变)</li>
</ul>
</li>
</ol>
</li>
<li>key 为 index 的问题<ol>
<li>添加/删除/排序 =&gt; 产生没有必要的真实 DOM 更新 ==&gt; 界面效果没问题, 但效率低</li>
<li>如果 item 界面还有输入框 =&gt; 产生错误的真实 DOM 更新 ==&gt; 界面有问题<br>注意: 如果不存在添加/删除/排序操作, 用 index 没有问题</li>
</ol>
</li>
<li>解决:<br>使用 item 数据的标识数据作为 key, 比如 id 属性值</li>
</ol>
<h2 id="NextTick-原理"><a href="#NextTick-原理" class="headerlink" title="NextTick 原理"></a>NextTick 原理</h2><p><code>nextTick</code> 可以让我们在下次 DOM 更新循环结束之后执行延迟回调，用于获得更新后的 DOM。</p>
<p>在 Vue 2.4 之前都是使用的 microtasks，但是 microtasks 的优先级过高，在某些情况下可能会出现比事件冒泡更快的情况，但如果都使用 macrotasks 又可能会出现渲染的性能问题。所以在新版本中，会默认使用 microtasks，但在特殊情况下会使用 macrotasks，比如 v-on。</p>
<p>对于实现 macrotasks ，会先判断是否能使用 <code>setImmediate</code> ，不能的话降级为 <code>MessageChannel</code> ，以上都不行的话就使用 <code>setTimeout</code></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (<span class="keyword">typeof</span> setImmediate !== <span class="string">&#x27;undefined&#x27;</span> &amp;&amp; isNative(setImmediate)) &#123;</span><br><span class="line">  macroTimerFunc = <span class="function">() =&gt;</span> &#123;</span><br><span class="line">    setImmediate(flushCallbacks)</span><br><span class="line">  &#125;</span><br><span class="line">&#125; <span class="keyword">else</span> <span class="keyword">if</span> (</span><br><span class="line">  <span class="keyword">typeof</span> MessageChannel !== <span class="string">&#x27;undefined&#x27;</span> &amp;&amp;</span><br><span class="line">  (isNative(MessageChannel) ||</span><br><span class="line">    <span class="comment">// PhantomJS</span></span><br><span class="line">    MessageChannel.toString() === <span class="string">&#x27;[object MessageChannelConstructor]&#x27;</span>)</span><br><span class="line">) &#123;</span><br><span class="line">  <span class="keyword">const</span> channel = <span class="keyword">new</span> MessageChannel()</span><br><span class="line">  <span class="keyword">const</span> port = channel.port2</span><br><span class="line">  channel.port1.onmessage = flushCallbacks</span><br><span class="line">  macroTimerFunc = <span class="function">() =&gt;</span> &#123;</span><br><span class="line">    port.postMessage(<span class="number">1</span>)</span><br><span class="line">  &#125;</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">  macroTimerFunc = <span class="function">() =&gt;</span> &#123;</span><br><span class="line">    <span class="built_in">setTimeout</span>(flushCallbacks, <span class="number">0</span>)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="vue3-为什么比vue2-快"><a href="#vue3-为什么比vue2-快" class="headerlink" title="vue3 为什么比vue2 快"></a>vue3 为什么比vue2 快</h2><p><a target="_blank" rel="noopener" href="https://template-explorer.vuejs.org/#eyJzcmMiOiI8ZGl2PkhlbGxvIFdvcmxkPC9kaXY+XHJcbjxkaXY+SGVsbG8gV29ybGQ8L2Rpdj5cclxuPGRpdj5IZWxsbyBXb3JsZDwvZGl2PlxyXG48ZGl2PkhlbGxvIFdvcmxkPC9kaXY+XHJcbjxkaXY+e3ttZXNzfX08L2Rpdj4iLCJvcHRpb25zIjp7ImhvaXN0U3RhdGljIjp0cnVlfX0=">https://template-explorer.vuejs.org/#eyJzcmMiOiI8ZGl2PkhlbGxvIFdvcmxkPC9kaXY+XHJcbjxkaXY+SGVsbG8gV29ybGQ8L2Rpdj5cclxuPGRpdj5IZWxsbyBXb3JsZDwvZGl2PlxyXG48ZGl2PkhlbGxvIFdvcmxkPC9kaXY+XHJcbjxkaXY+e3ttZXNzfX08L2Rpdj4iLCJvcHRpb25zIjp7ImhvaXN0U3RhdGljIjp0cnVlfX0=</a></p>
<h3 id="proxy-响应式"><a href="#proxy-响应式" class="headerlink" title="proxy 响应式"></a>proxy 响应式</h3><h3 id="patchFlag-静态标记"><a href="#patchFlag-静态标记" class="headerlink" title="patchFlag 静态标记"></a>patchFlag 静态标记</h3><ul>
<li>编译模板时，动态节点做标记</li>
<li>标记，分为不同的类型，如TEXT PROPS</li>
<li>diff 算法时，可以区分静态节点，以及不同类型的动态节点</li>
</ul>
<h3 id="hoistStatic-静态提升"><a href="#hoistStatic-静态提升" class="headerlink" title="hoistStatic 静态提升"></a>hoistStatic 静态提升</h3><ul>
<li>将静态节点的定义，提升到父作用域，缓存起来</li>
<li>多个相邻的静态节点，会被合并起来 变异优化</li>
<li>典型的拿空间换时间的优化策略</li>
</ul>
<h3 id="cacheHandler-缓存函数"><a href="#cacheHandler-缓存函数" class="headerlink" title="cacheHandler 缓存函数"></a>cacheHandler 缓存函数</h3><ul>
<li>缓存事件</li>
</ul>
<h3 id="SSR优化-预字符串化处理（大量的静态节点）"><a href="#SSR优化-预字符串化处理（大量的静态节点）" class="headerlink" title="SSR优化 预字符串化处理（大量的静态节点）"></a>SSR优化 预字符串化处理（大量的静态节点）</h3><ul>
<li>静态节点直接输出 绕过vdom</li>
<li>动态节点还是动态渲染</li>
</ul>
<h3 id="tree-shaking"><a href="#tree-shaking" class="headerlink" title="tree shaking"></a>tree shaking</h3><h2 id="Composition-API和-React-Hooks-对比"><a href="#Composition-API和-React-Hooks-对比" class="headerlink" title="Composition API和 React Hooks 对比"></a>Composition API和 React Hooks 对比</h2><p>从 React Hook 从实现的角度来看，React Hook 是基于 useState 的调用顺序来确定下一个 re 渲染时间状态从哪个 useState 开始，所以有以下几个限制</p>
<ul>
<li>不在循环中、条件、调用嵌套函数 Hook</li>
<li>你必须确保它总是在你这边 React Top level 调用函数 Hook</li>
<li>使用效果、使用备忘录 依赖关系必须手动确定</li>
</ul>
<p>和 Composition API 是基于 Vue 的响应系统，和 React Hook 相比</p>
<ul>
<li>在设置函数中，一个组件实例只调用一次设置，而 React Hook 每次重新渲染时，都需要调用 Hook，给 React 带来的 GC 比 Vue 更大的压力，性能也相对 Vue 对我来说也比较慢</li>
<li>Compositon API 你不必担心调用的顺序，它也可以在循环中、条件、在嵌套函数中使用</li>
<li>响应式系统自动实现依赖关系收集，而且组件的性能优化是由 Vue 内部完成的，而 React Hook 的依赖关系需要手动传递，并且依赖关系的顺序必须得到保证，让路 useEffect、useMemo 等等，否则组件性能会因为依赖关系不正确而下降。</li>
</ul>
<p>虽然Compoliton API看起来像React Hook来使用，但它的设计思路也是React Hook的参考。</p>
<h2 id="vue3有哪些新的组件"><a href="#vue3有哪些新的组件" class="headerlink" title="vue3有哪些新的组件"></a>vue3有哪些新的组件</h2><ul>
<li><p>Fragment<br>组件可以没有根标签, 内部会将多个标签包含在一个Fragment虚拟元素中<br>减少标签层级, 减小内存占用</p>
</li>
<li><p>Teleport</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">&lt;teleport to=<span class="string">&quot;移动位置&quot;</span>&gt;</span><br><span class="line"></span><br><span class="line">   &lt;div v-<span class="keyword">if</span>=<span class="string">&quot;isShow&quot;</span> <span class="class"><span class="keyword">class</span></span>=<span class="string">&quot;mask&quot;</span>&gt;</span><br><span class="line">       &lt;div <span class="class"><span class="keyword">class</span></span>=<span class="string">&quot;dialog&quot;</span>&gt;</span><br><span class="line">           &lt;h3&gt;我是一个弹窗&lt;/h3&gt;</span><br><span class="line">           &lt;button @click=<span class="string">&quot;isShow = false&quot;</span>&gt;关闭弹窗&lt;/button&gt;</span><br><span class="line">       &lt;/div&gt;</span><br><span class="line">   &lt;/div&gt;</span><br><span class="line">&lt;/teleport&gt;</span><br><span class="line"></span><br></pre></td></tr></table></figure>
</li>
<li><p>Suspense</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123;defineAsyncComponent&#125; <span class="keyword">from</span> <span class="string">&#x27;vue&#x27;</span></span><br><span class="line"> <span class="keyword">const</span> Child = defineAsyncComponent(<span class="function">()=&gt;</span><span class="keyword">import</span>(<span class="string">&#x27;./components/Child.vue&#x27;</span>))</span><br><span class="line"></span><br><span class="line"> &lt;template&gt;</span><br><span class="line">    &lt;div <span class="class"><span class="keyword">class</span></span>=<span class="string">&quot;app&quot;</span>&gt;</span><br><span class="line">        &lt;h3&gt;我是App组件&lt;/h3&gt;</span><br><span class="line">        &lt;Suspense&gt;</span><br><span class="line">            &lt;template v-slot:<span class="keyword">default</span>&gt;</span><br><span class="line">                &lt;Child/&gt;</span><br><span class="line">            &lt;/template&gt;</span><br><span class="line">            &lt;template v-slot:fallback&gt;</span><br><span class="line">                &lt;h3&gt;加载中.....&lt;/h3&gt;</span><br><span class="line">            &lt;/template&gt;</span><br><span class="line">        &lt;/Suspense&gt;</span><br><span class="line">    &lt;/div&gt;</span><br><span class="line"> &lt;/template&gt;</span><br></pre></td></tr></table></figure>

</li>
</ul>
<h2 id="首屏加载如何优化的"><a href="#首屏加载如何优化的" class="headerlink" title="首屏加载如何优化的"></a>首屏加载如何优化的</h2><ul>
<li>减小入口文件积</li>
<li>静态资源本地缓存</li>
<li>UI框架按需加载</li>
<li>图片资源的压缩</li>
<li>组件重复打包</li>
<li>开启GZip压缩</li>
<li>使用SSR</li>
</ul>
<h2 id="Vue的diff算法"><a href="#Vue的diff算法" class="headerlink" title="Vue的diff算法"></a>Vue的diff算法</h2><p>diff 算法是一种通过同层的树节点进行比较的高效算法</p>
<p>diff整体策略为：深度优先，同层比较<br>比较只会在同层级进行, 不会跨层级比较<br>比较的过程中，循环从两边向中间收拢</p>
<ul>
<li>当数据发生改变时，订阅者watcher就会调用patch给真实的DOM打补丁</li>
<li>通过isSameVnode进行判断，相同则调用patchVnode方法</li>
<li>patchVnode做了以下操作：<ul>
<li>找到对应的真实dom，称为el</li>
<li>如果都有都有文本节点且不相等，将el文本节点设置为Vnode的文本节点</li>
<li>如果oldVnode有子节点而VNode没有，则删除el子节点</li>
<li>如果oldVnode没有子节点而VNode有，则将VNode的子节点真实化后添加到el</li>
<li>如果两者都有子节点，则执行updateChildren函数比较子节点</li>
</ul>
</li>
<li>updateChildren主要做了以下操作：<ul>
<li>设置新旧VNode的头尾指针</li>
<li>新旧头尾指针进行比较，循环向中间靠拢，根据情况调用patchVnode进行patch重复流程、调用createElem创建一个新节点，从哈希表寻找 key一致的VNode 节点再分情况操作</li>
</ul>
</li>
</ul>
<p>diff整体策略为：深度优先，同层比较<br>比较只会在同层级进行, 不会跨层级比较<br>比较的过程中，循环从两边向中间收拢</p>
<ul>
<li>当数据发生改变时，订阅者watcher就会调用patch给真实的DOM打补丁</li>
<li>通过isSameVnode进行判断，相同则调用patchVnode方法</li>
<li>patchVnode做了以下操作：<ul>
<li>找到对应的真实dom，称为el</li>
<li>如果都有都有文本节点且不相等，将el文本节点设置为Vnode的文本节点</li>
<li>如果oldVnode有子节点而VNode没有，则删除el子节点</li>
<li>如果oldVnode没有子节点而VNode有，则将VNode的子节点真实化后添加到el</li>
<li>如果两者都有子节点，则执行updateChildren函数比较子节点</li>
</ul>
</li>
<li>updateChildren主要做了以下操作：<ul>
<li>设置新旧VNode的头尾指针</li>
<li>新旧头尾指针进行比较，循环向中间靠拢，根据情况调用patchVnode进行patch重复流程、调用createElem创建一个新节点，从哈希表寻找 key一致的VNode 节点再分情况操作</li>
</ul>
</li>
</ul>
<h2 id="vue-二次封装UI组件库"><a href="#vue-二次封装UI组件库" class="headerlink" title="vue 二次封装UI组件库"></a>vue 二次封装UI组件库</h2><p>1 二次组件必须继承原有组件的所有特性。<br>2 二次组件名必须见名知意。<br>3 自定义暴露出来的接口越简单越好。<br>4 留有自定义插槽，让用户可以自己选择。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">&lt;template&gt;</span><br><span class="line">  &lt;div class&#x3D;&quot;my-input&quot;&gt;</span><br><span class="line">    &lt;el-input v-bind&#x3D;&quot;$attrs&quot; ref&#x3D;&quot;inp&quot;&gt;</span><br><span class="line">      &lt;template #[slotName]&#x3D;&quot;slotProps&quot; v-for&#x3D;&quot;(slot, slotName) in $slots&quot; &gt;</span><br><span class="line">          &lt;slot :name&#x3D;&quot;slotName&quot; v-bind&#x3D;&quot;slotProps&quot;&#x2F;&gt;</span><br><span class="line">      &lt;&#x2F;template&gt;</span><br><span class="line">    &lt;&#x2F;el-input&gt;</span><br><span class="line">  &lt;&#x2F;div&gt;</span><br><span class="line">&lt;&#x2F;template&gt;</span><br><span class="line"></span><br><span class="line">&lt;script setup&gt;</span><br><span class="line"></span><br><span class="line">&lt;&#x2F;script&gt;</span><br><span class="line"></span><br><span class="line"> script setup 语法  组件实例属性和方法无法调用</span><br><span class="line"> 可以通过defineExpose显式公开ref属性</span><br><span class="line"></span><br><span class="line"> 可以使用选项式语法 绑定this</span><br><span class="line"> &lt;script&gt;</span><br><span class="line">export default &#123;</span><br><span class="line">  mounted() &#123;</span><br><span class="line">  const entries &#x3D; Object.entries(this.$refs.inp);</span><br><span class="line">  for(let [key,value] of entries)&#123;</span><br><span class="line">    this[key] &#x3D; value</span><br><span class="line">  &#125;</span><br><span class="line"> </span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line">&lt;&#x2F;script&gt;</span><br></pre></td></tr></table></figure>

<h2 id="vue面试题"><a href="#vue面试题" class="headerlink" title="vue面试题"></a>vue面试题</h2><p>[vue面试题]<a target="_blank" rel="noopener" href="https://github.com/57code/vue-interview">https://github.com/57code/vue-interview</a></p>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kity@2.0.4/dist/kity.min.js"></script><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kityminder-core@1.4.50/dist/kityminder.core.min.js"></script><script defer="true" type="text/javascript" src="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.js"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.css">
      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="https://gz1234.gitee.io/2020/10/29/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E9%A2%98/webpack%E9%9D%A2%E8%AF%95%E9%A2%98/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="郭泽">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="郭泽">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | 郭泽">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2020/10/29/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E9%A2%98/webpack%E9%9D%A2%E8%AF%95%E9%A2%98/" class="post-title-link" itemprop="url">webpack面试题</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2020-10-29 11:09:28" itemprop="dateCreated datePublished" datetime="2020-10-29T11:09:28+08:00">2020-10-29</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2023-06-06 09:12:27" itemprop="dateModified" datetime="2023-06-06T09:12:27+08:00">2023-06-06</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E9%A2%98/" itemprop="url" rel="index"><span itemprop="name">前端面试题</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h2 id="cache-loader-和-hard-source-webpack-plugin-的-区-别是-什-么-？"><a href="#cache-loader-和-hard-source-webpack-plugin-的-区-别是-什-么-？" class="headerlink" title="cache-loader 和 hard-source-webpack-plugin 的 区 别是 什 么 ？"></a>cache-loader 和 hard-source-webpack-plugin 的 区 别是 什 么 ？</h2><h2 id="Babel-profill：怎么把-es6-转化为-es5，兼容-ie"><a href="#Babel-profill：怎么把-es6-转化为-es5，兼容-ie" class="headerlink" title="Babel-profill：怎么把 es6 转化为 es5，兼容 ie"></a>Babel-profill：怎么把 es6 转化为 es5，兼容 ie</h2><ul>
<li><p>Parser 解析<br>第一步主要是将 ES6 语法解析为 AST 抽象语法树。</p>
</li>
<li><p>Transformer 转换<br>第二步是将打散的 AST 语法通过配置好的 plugins 和 presets 转换成新的 AST 语法。这一步主要是由 babel-transform 插件完成。plugins 和 presets 通常在 .babelrc 文件中配置。</p>
</li>
<li><p>Generator 生成<br>第三步是将新的 AST 语法树对象再生成浏览器都可以识别的 ES5 语法。这一步主要是由 babel-generator 插件完成</p>
<p>我们写的原始代码、<br>1.–&gt;parse(解析)阶段，通过–&gt;babylon 工具做代码的解析转换为–&gt;AST(抽象语法树)<br>2.–&gt;transform(改变)阶段，<br>通过—&gt;babel-traverse 工具，把代码转为 es5 或 es3，如果 babel 没有达到我们的预期 babel 还提供了很多插件可以继续转换到–&gt;AST 阶段<br>3.AST(抽象语法树)阶段–&gt;<br>通过 plugin 工具处理过滤代码(删除代码、增加代码)，在 AST 语法树上改，改完之后到—&gt;AST 这个树上，到—&gt;generator 阶段，<br>4.—&gt;generator(生成、生产)阶段<br>通过—&gt;babel-generator 工具处理还原代码，变成浏览器所识别的文本到—&gt;code,我们看到的最后编译后的代码</p>
</li>
</ul>
<h2 id="webpack-plugin-的-原-理-是-什-么-？"><a href="#webpack-plugin-的-原-理-是-什-么-？" class="headerlink" title="webpack plugin 的 原 理 是 什 么 ？"></a>webpack plugin 的 原 理 是 什 么 ？</h2><h2 id="plugin-中-有-异-步-请-求-会-阻-塞-后-面-的-plugin-吗-？"><a href="#plugin-中-有-异-步-请-求-会-阻-塞-后-面-的-plugin-吗-？" class="headerlink" title="plugin 中 有 异 步 请 求 会 阻 塞 后 面 的 plugin 吗 ？"></a>plugin 中 有 异 步 请 求 会 阻 塞 后 面 的 plugin 吗 ？</h2><h2 id="webpack-的-sourcemap-？"><a href="#webpack-的-sourcemap-？" class="headerlink" title="webpack 的 sourcemap ？"></a>webpack 的 sourcemap ？</h2><p>sourcemap 是编译后代码和原始代码的映射在生产环境，sourcemap 不能发布到线上。<br>通常的做法是上传到异常监控服务器。用于解析线上错误信息。<br><img src="/2020/10/29/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E9%A2%98/webpack%E9%9D%A2%E8%AF%95%E9%A2%98/sourceMap.png" alt="sourceMap生成环境使用"></p>
<h2 id="hard-sour-c-e-webpack-plugin-是-怎-么-做-缓-存-的-？-修-改-文-件-后-会怎-么-样-？"><a href="#hard-sour-c-e-webpack-plugin-是-怎-么-做-缓-存-的-？-修-改-文-件-后-会怎-么-样-？" class="headerlink" title="hard-sour c e-webpack-plugin 是 怎 么 做 缓 存 的 ？ 修 改 文 件 后 会怎 么 样 ？"></a>hard-sour c e-webpack-plugin 是 怎 么 做 缓 存 的 ？ 修 改 文 件 后 会怎 么 样 ？</h2><h2 id="parallel-的-原-理-是-什-么-？-多-个-子-进-程-怎-么-通-信-？"><a href="#parallel-的-原-理-是-什-么-？-多-个-子-进-程-怎-么-通-信-？" class="headerlink" title="parallel 的 原 理 是 什 么 ？ 多 个 子 进 程 怎 么 通 信 ？"></a>parallel 的 原 理 是 什 么 ？ 多 个 子 进 程 怎 么 通 信 ？</h2><h2 id="webpack-与-gulp-grunt"><a href="#webpack-与-gulp-grunt" class="headerlink" title="webpack 与 gulp/grunt"></a>webpack 与 gulp/grunt</h2><ul>
<li>webpack 是一种模块化打包工具，主要用于模块化方案，预编译模块的方案。</li>
<li>Grunt/Gulp 是非模块化打包工具, 是工具链，可以配合各种插件做 js 压缩，css 压缩，less 编译 替代手工实现自动化工作</li>
</ul>
<h2 id="webpack-的核心概念"><a href="#webpack-的核心概念" class="headerlink" title="webpack 的核心概念"></a>webpack 的核心概念</h2><ul>
<li>Entry：入口，Webpack 进行打包的起始点(文件)</li>
<li>Output：出口，webpack 编译打包生成的 bundle(文件)</li>
<li>Loader：模块加载(转换)器，将非 js 模块包装成 webpack 能理解的 js 模块</li>
<li>Plugin：插件，在 Webpack 构建流程中的特定时机插入具有特定功能的代码</li>
<li>Module：模块，在 Webpack 眼里一切皆模块，默认只识别 js 文件, 如果是其它类型文件利用对应的 loader 转换为 js 模块</li>
<li>Chunk/bundle：代码块/束，一个 Chunk 由多个模块组合而成, 最终浏览器执行的是 webpack 打包生成的 chunk 文件</li>
</ul>
<h2 id="有哪些常见的Loader？他们是解决什么问题的？"><a href="#有哪些常见的Loader？他们是解决什么问题的？" class="headerlink" title="有哪些常见的Loader？他们是解决什么问题的？"></a>有哪些常见的Loader？他们是解决什么问题的？</h2><ul>
<li>file-loader：把文件输出到一个文件夹中，在代码中通过相对 URL 去引用输出的文件</li>
<li>url-loader：和 file-loader 类似，但是能在文件很小的情况下以 base64 的方式把文件内容注入到代码中去</li>
<li>source-map-loader：加载额外的 Source Map 文件，以方便断点调试</li>
<li>image-loader：加载并且压缩图片文件</li>
<li>babel-loader：把 ES6 转换成 ES5</li>
<li>css-loader：加载 CSS，支持模块化、压缩、文件导入等特性</li>
<li>style-loader：把 CSS 代码注入到 JavaScript 中，通过 DOM 操作去加载 CSS。</li>
<li>eslint-loader：通过 ESLint 检查 JavaScript 代码</li>
</ul>
<h2 id="有哪些常见的Plugin？他们是解决什么问题的？"><a href="#有哪些常见的Plugin？他们是解决什么问题的？" class="headerlink" title="有哪些常见的Plugin？他们是解决什么问题的？"></a>有哪些常见的Plugin？他们是解决什么问题的？</h2><p>clean-webpack-plugin：重新打包自动清空 dist 目录<br>compression-webpack-plugin: 将前端打包好的资源文件进一步压缩，生成指定的、体积更小的压缩文件<br>html-webpack-plugin：可以复制一个有结构的html文件，并自动引入打包输出的所有资源（JS/CSS）<br>mini-css-extract-plugin：提取 js 中的 css 成单独文件<br>optimize-css-assets-webpack-plugin, terser-webpack-plugin：压缩css<br>uglifyjs-webpack-plugin：压缩js<br>define-plugin: 用于定义环境变量<br>commons-chunk-plugin：提取公共代码<br>Copy-webpack-plugin: 用于将静态文件直接复制到输出目录中。<br>webpack-bundle-analyzer: 用于分析并可视化打包后的模块大小和依赖关系</p>
<h2 id="webpack-模块化打包的基本流程"><a href="#webpack-模块化打包的基本流程" class="headerlink" title="webpack 模块化打包的基本流程"></a>webpack 模块化打包的基本流程</h2><ul>
<li>连接: webpack 从入口 JS 开始, 递归查找出所有相关联的模块, 并<code>连接</code>起来形成一个图(网)的结构</li>
<li>编译: 将 JS 模块中的模块化语法<code>编译</code>为浏览器可以直接运行的模块语法(当然其它类型资源也会处理)</li>
<li>合并: 将图中所有编译过的模块<code>合并</code>成一个或少量几个 bundle 文件, 而浏览器运行是打包生成的 bundle 文件</li>
</ul>
<h2 id="比较-loader-与-plugin"><a href="#比较-loader-与-plugin" class="headerlink" title="比较 loader 与 plugin"></a>比较 loader 与 plugin</h2><p><strong>不同的作用</strong></p>
<ul>
<li><strong>Loader</strong>直译为”加载器”。Webpack 将一切文件视为模块，但是 webpack 原生是只能解析 js 文件，如果想将其他文件也打包的话，就会用到 loader。 所以 Loader 的作用是让 webpack 拥有了加载和解析非 JavaScript 文件的能力。</li>
<li><strong>Plugin</strong>直译为”插件”。Plugin 可以扩展 webpack 的功能，让 webpack 具有更多的灵活性。 在 Webpack 运行的生命周期中会广播出许多事件，Plugin 可以监听这些事件，在合适的时机通过 Webpack 提供的 API 改变输出结果。</li>
</ul>
<p><strong>不同的用法</strong></p>
<ul>
<li><strong>Loader</strong>在 module.rules 中配置，也就是说他作为模块的解析规则而存在。 类型为数组，每一项都是一个 Object，里面描述了对于什么类型的文件（test），使用什么加载(loader)和使用的参数（options）</li>
<li><strong>Plugin</strong>在 plugins 中单独配置。 类型为数组，每一项是一个 plugin 的实例，参数都通过构造函数传入。</li>
</ul>
<h2 id="webpack-打包后文件体积过大怎么办"><a href="#webpack-打包后文件体积过大怎么办" class="headerlink" title="webpack 打包后文件体积过大怎么办"></a>webpack 打包后文件体积过大怎么办</h2><ul>
<li>异步加载模块（代码分割）；提取第三方库（使用 cdn 或者 vender）；代码压缩；去除不必要的插件；去除 devtool 选项等等</li>
</ul>
<h2 id="webpack-的-Tree-shaking-是什么"><a href="#webpack-的-Tree-shaking-是什么" class="headerlink" title="webpack 的 Tree-shaking 是什么?"></a>webpack 的 Tree-shaking 是什么?</h2><ul>
<li><p>Webpack 在打包 js 模块时, 会将模块中向外暴露但没有被使用的功能从打包文件中移除<br>2 个前提:<br>必须是使用 ES6 的 export 向外暴露的<br>必须进行 JS 的压缩处理</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">optimization: &#123;<span class="attr">usedExports</span>: <span class="literal">true</span>,&#125;</span><br></pre></td></tr></table></figure>

</li>
</ul>
<h2 id="live-reload-与-hot-realod-HMR"><a href="#live-reload-与-hot-realod-HMR" class="headerlink" title="live-reload 与 hot-realod/HMR"></a>live-reload 与 hot-realod/HMR</h2><ul>
<li>相同点: 代码修改后都会自动重新编译打包</li>
<li>不同点:<ul>
<li>live-reload: 刷新整体页面, 从而查看到最新代码的效果, 页面状态全部都是新的</li>
<li>Hot-reload: 没有刷新整个页面, 只是加载了修改模块的打包文件并运行,从而更新页面的局部界面, 整个界面的其它部分的状态还在</li>
</ul>
</li>
</ul>
<p><a target="_blank" rel="noopener" href="https://juejin.cn/post/7011765691812413476">webpack</a></p>
<h2 id="resolve-alias-字符串替换"><a href="#resolve-alias-字符串替换" class="headerlink" title="resolve.alias 字符串替换"></a>resolve.alias 字符串替换</h2><h2 id="webpack文件指纹策略：hash-chunkhash-contenthash"><a href="#webpack文件指纹策略：hash-chunkhash-contenthash" class="headerlink" title="webpack文件指纹策略：hash chunkhash contenthash"></a>webpack文件指纹策略：hash chunkhash contenthash</h2><p>hash策略：是以项目为单位的，项目内容改变则会生成新的hash，内容不变则hash不变<br>​<br>chunkhash策略：是以chunk为单位的，当一个文件内容改变，则整个相应的chunk组模块的hash回发生改变<br>​<br>contenthash策略：是以自身内容为单为的<br>​<br>推荐使用：css ：contenthash<br>​<br>•js：chunkhash</p>
<h2 id="module，chunk和bundle的区别是什么"><a href="#module，chunk和bundle的区别是什么" class="headerlink" title="module，chunk和bundle的区别是什么"></a>module，chunk和bundle的区别是什么</h2><ul>
<li>在src目录下，我们手写的一个个文件，无论是EcmaScript还是CommonJS，它们都是module。</li>
<li>当我们写的module源文件传到webpack进行打包时，webpack会根据文件引用关系（entry import splitChunk）生成chunk文<br>件，webpack会对这个chunk文件进行一些操作(相当于是内存文件)。</li>
<li>webpack处理好chunk文件后，最后会输出bundle文件，这个bundle文件包含了经过加载和编译<br>的最终源文件，所以可以直接在浏览器中运行。</li>
</ul>
<h2 id="webpack-构建优化"><a href="#webpack-构建优化" class="headerlink" title="webpack 构建优化"></a>webpack 构建优化</h2><ul>
<li><p>优化babel-loader use:[‘babel-loader?cacheDirectory’] 开启缓存  明确文件范围 include  exclude</p>
</li>
<li><p>IgnorePlugin 避免引入无用的插件忽略第三方包指定目录，让这些指定目录不要被打包进去</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">plugins:[</span><br><span class="line">  <span class="comment">//moment这个库中，如果引用了./locale/目录的内容，就忽略掉，不会打包进去 手动引入需要的语言包</span></span><br><span class="line">  <span class="keyword">new</span> Webpack.IgnorePlugin(<span class="regexp">/\.\/locale/</span>,<span class="regexp">/moment/</span>),</span><br><span class="line">  </span><br><span class="line">]</span><br></pre></td></tr></table></figure>
</li>
<li><p>noParse 避免重复打包， 一般使用.min.js结尾的文件，都是已经经过模块化处理的，不再参与loader或webpack的解析及打包</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>:&#123;</span><br><span class="line">  noParse:[] <span class="comment">// RegExp [RegExp] function(resource) string [string]</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>happyPack 多进程打包  </p>
</li>
</ul>
<h2 id="升级-webpack5-以及周边插件后，代码需要做出的调整"><a href="#升级-webpack5-以及周边插件后，代码需要做出的调整" class="headerlink" title="升级 webpack5 以及周边插件后，代码需要做出的调整"></a>升级 webpack5 以及周边插件后，代码需要做出的调整</h2><ul>
<li>package.json 的 dev-server 命令改了”dev”: “webpack serve –config buildwebpack.dev.js”</li>
<li>升级新版本const { merge } = require(‘webpack-merge’)</li>
<li>升级新版本const [ CleanWebpackPlugin }= require(‘clean-webpack-plugin’)</li>
<li>module.rules’中loader: [‘xxx-loader’] 换成use: [‘xxx-loader’]</li>
<li>filename:bundle.[contenthash:8].js’、其中h’ 小写，不能大写</li>
</ul>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kity@2.0.4/dist/kity.min.js"></script><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kityminder-core@1.4.50/dist/kityminder.core.min.js"></script><script defer="true" type="text/javascript" src="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.js"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.css">
      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="https://gz1234.gitee.io/2020/10/29/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E9%A2%98/%E5%89%8D%E7%AB%AF%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96%E9%9D%A2%E8%AF%95%E9%A2%98/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="郭泽">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="郭泽">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | 郭泽">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2020/10/29/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E9%A2%98/%E5%89%8D%E7%AB%AF%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96%E9%9D%A2%E8%AF%95%E9%A2%98/" class="post-title-link" itemprop="url">前端性能优化面试题</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2020-10-29 11:09:28" itemprop="dateCreated datePublished" datetime="2020-10-29T11:09:28+08:00">2020-10-29</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2023-05-16 15:50:33" itemprop="dateModified" datetime="2023-05-16T15:50:33+08:00">2023-05-16</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E9%A2%98/" itemprop="url" rel="index"><span itemprop="name">前端面试题</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="性能优化琐碎事"><a href="#性能优化琐碎事" class="headerlink" title="性能优化琐碎事"></a>性能优化琐碎事</h1><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">注意：该知识点属于性能优化领域。</span><br></pre></td></tr></table></figure>

<h2 id="计算图片大小"><a href="#计算图片大小" class="headerlink" title="计算图片大小"></a>计算图片大小</h2><p>对于一张 100 <em>100 像素的图片来说，图像上有 10000 个像素点，如果每个像素的值是 <strong>RGBA</strong> 存储的话，那么也就是说每个像素有 4 个通道，每个通道 1 个字节（8 位 = 1个字节），所以该图片大小大概为 39KB（10000</em> 1 * 4 / 1024）。</p>
<p>但是在实际项目中，一张图片可能并不需要使用那么多颜色去显示，我们可以通过减少每个像素的调色板来相应缩小图片的大小。</p>
<p>了解了如何计算图片大小的知识，那么对于如何优化图片，想必大家已经有 2 个思路了：</p>
<ul>
<li><strong>减少像素点</strong></li>
<li><strong>减少每个像素点能够显示的颜色</strong></li>
</ul>
<h2 id="图片加载优化"><a href="#图片加载优化" class="headerlink" title="图片加载优化"></a>图片加载优化</h2><ol>
<li>不用图片。很多时候会使用到很多修饰类图片，其实这类修饰图片完全可以用 CSS 去代替。</li>
<li>对于移动端来说，屏幕宽度就那么点，完全没有必要去加载原图浪费带宽。一般图片都用 CDN 加载，可以计算出适配屏幕的宽度，然后去请求相应裁剪好的图片。</li>
<li>小图使用 base64 格式</li>
<li>将多个图标文件整合到一张图片中（雪碧图）</li>
<li>选择正确的图片格式：<ul>
<li>对于能够显示 WebP 格式的浏览器尽量使用 WebP 格式。因为 WebP 格式具有更好的图像数据压缩算法，能带来更小的图片体积，而且拥有肉眼识别无差异的图像质量，缺点就是兼容性并不好</li>
<li>小图使用 PNG，其实对于大部分图标这类图片，完全可以使用 SVG 代替</li>
<li>照片使用 JPEG</li>
</ul>
</li>
</ol>
<h2 id="DNS-预解析"><a href="#DNS-预解析" class="headerlink" title="DNS 预解析"></a>DNS 预解析</h2><p>DNS 解析也是需要时间的，可以通过预解析的方式来预先获得域名所对应的 IP。</p>
<p>在框架中 要提取代码中的域名地址添加到HTML</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br></pre></td><td class="code"><pre><span class="line">&#123;<span class="comment">/* &lt;link rel=&quot;dns-prefetch&quot; href=&quot;//yuchengkai.cn&quot;&gt; */</span>&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> fs = <span class="built_in">require</span>(<span class="string">&quot;fs&quot;</span>);</span><br><span class="line"><span class="keyword">const</span> &#123; parse &#125; = <span class="built_in">require</span>(<span class="string">&quot;node-html-parser&quot;</span>);</span><br><span class="line"><span class="keyword">const</span> &#123; glob &#125; = <span class="built_in">require</span>(<span class="string">&quot;glob&quot;</span>);</span><br><span class="line"><span class="keyword">const</span> urlRegex = <span class="built_in">require</span>(<span class="string">&quot;url-regex&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> urlPatten = <span class="regexp">/(https?:\/\/[^/]*)/i</span>;</span><br><span class="line"><span class="keyword">const</span> urls = <span class="keyword">new</span> <span class="built_in">Set</span>();</span><br><span class="line"><span class="comment">// 遍历文件查找 域名</span></span><br><span class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">function</span> <span class="title">searchDomain</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> files = <span class="keyword">await</span> glob(<span class="string">&quot;dist/**/*.&#123;html,css,js&#125;&quot;</span>);</span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">const</span> file <span class="keyword">of</span> files) &#123;</span><br><span class="line">    <span class="keyword">const</span> source = fs.readFilesync(file, <span class="string">&quot;utf-8&quot;</span>);</span><br><span class="line">    <span class="keyword">const</span> matches = source.match(urlRegex(&#123; <span class="attr">strict</span>: <span class="literal">true</span> &#125;));</span><br><span class="line">    <span class="keyword">if</span> (matches) &#123;</span><br><span class="line">      matches.forEach(<span class="function">(<span class="params">url</span>) =&gt;</span> &#123;</span><br><span class="line">        <span class="keyword">const</span> match = url.match(urlPatten);</span><br><span class="line">        <span class="keyword">if</span> (match &amp;&amp; match[<span class="number">1</span>]) &#123;</span><br><span class="line">          urls.add(match[<span class="number">1</span>]);</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">function</span> <span class="title">insertLinks</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> files = <span class="keyword">await</span> glob(<span class="string">&quot;dist/**/*.html&quot;</span>);</span><br><span class="line">  <span class="keyword">const</span> links = [...urls]</span><br><span class="line">    .map(<span class="function">(<span class="params">url</span>) =&gt;</span> <span class="string">`&lt;link rel=&quot;dns-prefetch&quot; href=&quot;<span class="subst">$&#123;url&#125;</span>&quot; /&gt;`</span>)</span><br><span class="line">    .join(<span class="string">&quot;\n&quot;</span>);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">const</span> file <span class="keyword">of</span> files) &#123;</span><br><span class="line">    <span class="keyword">const</span> html = fs.readFileSync(file, <span class="string">&quot;utf-8&quot;</span>);</span><br><span class="line">    <span class="keyword">const</span> root = parse(html);</span><br><span class="line">    <span class="keyword">const</span> head = root.querySelector(<span class="string">&quot;head&quot;</span>);</span><br><span class="line">    head.insertAdjacentHTML(<span class="string">&quot;afterbegin&quot;</span>, links);</span><br><span class="line">    fs.writeFileSync(file, root.toString());</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">function</span> <span class="title">main</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line"> <span class="keyword">await</span> searchDomain();</span><br><span class="line"> <span class="keyword">await</span> insertLinks();</span><br><span class="line">&#125;</span><br><span class="line">main();</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h2 id="节流"><a href="#节流" class="headerlink" title="节流"></a>节流</h2><p>考虑一个场景，滚动事件中会发起网络请求，但是我们并不希望用户在滚动过程中一直发起请求，而是隔一段时间发起一次，对于这种情况我们就可以使用节流。</p>
<h2 id="防抖"><a href="#防抖" class="headerlink" title="防抖"></a>防抖</h2><p>考虑一个场景，有一个按钮点击会触发网络请求，但是我们并不希望每次点击都发起网络请求，而是当用户点击按钮一段时间后没有再次点击的情况才去发起网络请求，对于这种情况我们就可以使用防抖。</p>
<h2 id="预加载"><a href="#预加载" class="headerlink" title="预加载"></a>预加载</h2><p>在开发中，可能会遇到这样的情况。有些资源不需要马上用到，但是希望尽早获取，这时候就可以使用预加载。</p>
<p>预加载其实是声明式的 <code>fetch</code> ，强制浏览器请求资源，并且不会阻塞 <code>onload</code> 事件，可以使用以下代码开启预加载</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;preload&quot;</span> <span class="attr">href</span>=<span class="string">&quot;http://example.com&quot;</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>预加载可以一定程度上降低首屏的加载时间，因为可以将一些不影响首屏但重要的文件延后加载，唯一缺点就是兼容性不好。</p>
<h2 id="预渲染"><a href="#预渲染" class="headerlink" title="预渲染"></a>预渲染</h2><p>可以通过预渲染将下载的文件预先在后台渲染，可以使用以下代码开启预渲染</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">link</span> <span class="attr">rel</span>=<span class="string">&quot;prerender&quot;</span> <span class="attr">href</span>=<span class="string">&quot;http://example.com&quot;</span>&gt;</span> </span><br></pre></td></tr></table></figure>

<p>预渲染虽然可以提高页面的加载速度，但是要确保该页面大概率会被用户在之后打开，否则就是白白浪费资源去渲染。</p>
<h2 id="懒执行"><a href="#懒执行" class="headerlink" title="懒执行"></a>懒执行</h2><p>懒执行就是将某些逻辑延迟到使用时再计算。该技术可以用于首屏优化，对于某些耗时逻辑并不需要在首屏就使用的，就可以使用懒执行。懒执行需要唤醒，一般可以通过定时器或者事件的调用来唤醒。</p>
<h2 id="懒加载"><a href="#懒加载" class="headerlink" title="懒加载"></a>懒加载</h2><p>懒加载就是将不关键的资源延后加载。</p>
<p>懒加载的原理就是只加载自定义区域（通常是可视区域，但也可以是即将进入可视区域）内需要加载的东西。对于图片来说，先设置图片标签的 <code>src</code> 属性为一张占位图，将真实的图片资源放入一个自定义属性中，当进入自定义区域时，就将自定义属性替换为 <code>src</code> 属性，这样图片就会去下载资源，实现了图片懒加载。</p>
<p>懒加载不仅可以用于图片，也可以使用在别的资源上。比如进入可视区域才开始播放视频等等。</p>
<h2 id="CDN"><a href="#CDN" class="headerlink" title="CDN"></a>CDN</h2><p>CDN 的原理是尽可能的在各个地方分布机房缓存数据，这样即使我们的根服务器远在国外，在国内的用户也可以通过国内的机房迅速加载资源。</p>
<p>因此，我们可以将静态资源尽量使用 CDN 加载，由于浏览器对于单个域名有并发请求上限，可以考虑使用多个 CDN 域名。并且对于 CDN 加载静态资源需要注意 CDN 域名要与主站不同，否则每次请求都会带上主站的 Cookie，平白消耗流量。</p>
<h1 id="Webpack-性能优化"><a href="#Webpack-性能优化" class="headerlink" title="Webpack 性能优化"></a>Webpack 性能优化</h1><p>在这一的章节中，我不会浪费篇幅给大家讲如何写配置文件。<strong>如果你想学习这方面的内容，那么完全可以去官网学习</strong>。在这部分的内容中，我们会聚焦于以下两个知识点，并且每一个知识点都属于高频考点：</p>
<ul>
<li>有哪些方式可以减少 Webpack 的打包时间</li>
<li>有哪些方式可以让 Webpack 打出来的包更小</li>
</ul>
<h2 id="减少-Webpack-打包时间"><a href="#减少-Webpack-打包时间" class="headerlink" title="减少 Webpack 打包时间"></a>减少 Webpack 打包时间</h2><h3 id="优化-Loader"><a href="#优化-Loader" class="headerlink" title="优化 Loader"></a>优化 Loader</h3><p>对于 Loader 来说，影响打包效率首当其冲必属 Babel 了。因为 Babel 会将代码转为字符串生成 AST，然后对 AST 继续进行转变最后再生成新的代码，项目越大，<strong>转换代码越多，效率就越低</strong>。当然了，我们是有办法优化的。</p>
<p>首先我们可以<strong>优化 Loader 的文件搜索范围</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  <span class="built_in">module</span>: &#123;</span><br><span class="line">    rules: [</span><br><span class="line">      &#123;</span><br><span class="line">        <span class="comment">// js 文件才使用 babel</span></span><br><span class="line">        test: <span class="regexp">/\.js$/</span>,</span><br><span class="line">        loader: <span class="string">&#x27;babel-loader&#x27;</span>,</span><br><span class="line">        <span class="comment">// 只在 src 文件夹下查找</span></span><br><span class="line">        include: [resolve(<span class="string">&#x27;src&#x27;</span>)],</span><br><span class="line">        <span class="comment">// 不会去查找的路径</span></span><br><span class="line">        exclude: <span class="regexp">/node_modules/</span></span><br><span class="line">      &#125;</span><br><span class="line">    ]</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>对于 Babel 来说，我们肯定是希望只作用在 JS 代码上的，然后 <code>node_modules</code> 中使用的代码都是编译过的，所以我们也完全没有必要再去处理一遍。</p>
<p>当然这样做还不够，我们还可以将 Babel 编译过的文件<strong>缓存</strong>起来，下次只需要编译更改过的代码文件即可，这样可以大幅度加快打包时间</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">loader: <span class="string">&#x27;babel-loader?cacheDirectory=true&#x27;</span></span><br></pre></td></tr></table></figure>

<h3 id="HappyPack"><a href="#HappyPack" class="headerlink" title="HappyPack"></a>HappyPack</h3><p>受限于 Node 是单线程运行的，所以 Webpack 在打包的过程中也是单线程的，特别是在执行 Loader 的时候，长时间编译的任务很多，这样就会导致等待的情况。</p>
<p><strong>HappyPack 可以将 Loader 的同步执行转换为并行的</strong>，这样就能充分利用系统资源来加快打包效率了</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>: &#123;</span><br><span class="line">  loaders: [</span><br><span class="line">    &#123;</span><br><span class="line">      test: <span class="regexp">/\.js$/</span>,</span><br><span class="line">      include: [resolve(<span class="string">&#x27;src&#x27;</span>)],</span><br><span class="line">      exclude: <span class="regexp">/node_modules/</span>,</span><br><span class="line">      <span class="comment">// id 后面的内容对应下面</span></span><br><span class="line">      loader: <span class="string">&#x27;happypack/loader?id=happybabel&#x27;</span></span><br><span class="line">    &#125;</span><br><span class="line">  ]</span><br><span class="line">&#125;,</span><br><span class="line">plugins: [</span><br><span class="line">  <span class="keyword">new</span> HappyPack(&#123;</span><br><span class="line">    id: <span class="string">&#x27;happybabel&#x27;</span>,</span><br><span class="line">    loaders: [<span class="string">&#x27;babel-loader?cacheDirectory&#x27;</span>],</span><br><span class="line">    <span class="comment">// 开启 4 个线程</span></span><br><span class="line">    threads: <span class="number">4</span></span><br><span class="line">  &#125;)</span><br><span class="line">]</span><br></pre></td></tr></table></figure>

<h3 id="DllPlugin"><a href="#DllPlugin" class="headerlink" title="DllPlugin"></a>DllPlugin</h3><p><strong>DllPlugin 可以将特定的类库提前打包然后引入</strong>。这种方式可以极大的减少打包类库的次数，只有当类库更新版本才有需要重新打包，并且也实现了将公共代码抽离成单独文件的优化方案。</p>
<p>接下来我们就来学习如何使用 DllPlugin</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 单独配置在一个文件中</span></span><br><span class="line"><span class="comment">// webpack.dll.conf.js</span></span><br><span class="line"><span class="keyword">const</span> path = <span class="built_in">require</span>(<span class="string">&#x27;path&#x27;</span>)</span><br><span class="line"><span class="keyword">const</span> webpack = <span class="built_in">require</span>(<span class="string">&#x27;webpack&#x27;</span>)</span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  entry: &#123;</span><br><span class="line">    <span class="comment">// 想统一打包的类库</span></span><br><span class="line">    vendor: [<span class="string">&#x27;react&#x27;</span>]</span><br><span class="line">  &#125;,</span><br><span class="line">  output: &#123;</span><br><span class="line">    path: path.join(__dirname, <span class="string">&#x27;dist&#x27;</span>),</span><br><span class="line">    filename: <span class="string">&#x27;[name].dll.js&#x27;</span>,</span><br><span class="line">    library: <span class="string">&#x27;[name]-[hash]&#x27;</span></span><br><span class="line">  &#125;,</span><br><span class="line">  plugins: [</span><br><span class="line">    <span class="keyword">new</span> webpack.DllPlugin(&#123;</span><br><span class="line">      <span class="comment">// name 必须和 output.library 一致</span></span><br><span class="line">      name: <span class="string">&#x27;[name]-[hash]&#x27;</span>,</span><br><span class="line">      <span class="comment">// 该属性需要与 DllReferencePlugin 中一致</span></span><br><span class="line">      context: __dirname,</span><br><span class="line">      path: path.join(__dirname, <span class="string">&#x27;dist&#x27;</span>, <span class="string">&#x27;[name]-manifest.json&#x27;</span>)</span><br><span class="line">    &#125;)</span><br><span class="line">  ]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>然后我们需要执行这个配置文件生成依赖文件，接下来我们需要使用 <code>DllReferencePlugin</code> 将依赖文件引入项目中</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// webpack.conf.js</span></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  <span class="comment">// ...省略其他配置</span></span><br><span class="line">  plugins: [</span><br><span class="line">    <span class="keyword">new</span> webpack.DllReferencePlugin(&#123;</span><br><span class="line">      context: __dirname,</span><br><span class="line">      <span class="comment">// manifest 就是之前打包出来的 json 文件</span></span><br><span class="line">      manifest: <span class="built_in">require</span>(<span class="string">&#x27;./dist/vendor-manifest.json&#x27;</span>),</span><br><span class="line">    &#125;)</span><br><span class="line">  ]</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="代码压缩"><a href="#代码压缩" class="headerlink" title="代码压缩"></a>代码压缩</h3><p>在 Webpack3 中，我们一般使用 <code>UglifyJS</code> 来压缩代码，但是这个是单线程运行的，为了加快效率，我们可以使用 <code>webpack-parallel-uglify-plugin</code> 来并行运行 <code>UglifyJS</code>，从而提高效率。</p>
<p>在 Webpack4 中，我们就不需要以上这些操作了，只需要将 <code>mode</code> 设置为 <code>production</code> 就可以默认开启以上功能。代码压缩也是我们必做的性能优化方案，当然我们不止可以压缩 JS 代码，还可以压缩 HTML、CSS 代码，并且在压缩 JS 代码的过程中，我们还可以通过配置实现比如删除 <code>console.log</code> 这类代码的功能。</p>
<h3 id="一些小的优化点"><a href="#一些小的优化点" class="headerlink" title="一些小的优化点"></a>一些小的优化点</h3><p>我们还可以通过一些小的优化点来加快打包速度</p>
<ul>
<li><code>resolve.extensions</code>：用来表明文件后缀列表，默认查找顺序是 <code>[&#39;.js&#39;, &#39;.json&#39;]</code>，如果你的导入文件没有添加后缀就会按照这个顺序查找文件。我们应该尽可能减少后缀列表长度，然后将出现频率高的后缀排在前面</li>
<li><code>resolve.alias</code>：可以通过别名的方式来映射一个路径，能让 Webpack 更快找到路径</li>
<li><code>module.noParse</code>：如果你确定一个文件下没有其他依赖，就可以使用该属性让 Webpack 不扫描该文件，这种方式对于大型的类库很有帮助</li>
</ul>
<h2 id="减少-Webpack-打包后的文件体积"><a href="#减少-Webpack-打包后的文件体积" class="headerlink" title="减少 Webpack 打包后的文件体积"></a>减少 Webpack 打包后的文件体积</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">注意：该内容也属于性能优化领域。</span><br></pre></td></tr></table></figure>

<h3 id="按需加载"><a href="#按需加载" class="headerlink" title="按需加载"></a>按需加载</h3><p>想必大家在开发 SPA 项目的时候，项目中都会存在十几甚至更多的路由页面。如果我们将这些页面全部打包进一个 JS 文件的话，虽然将多个请求合并了，但是同样也加载了很多并不需要的代码，耗费了更长的时间。那么为了首页能更快地呈现给用户，我们肯定是希望首页能加载的文件体积越小越好，<strong>这时候我们就可以使用按需加载，将每个路由页面单独打包为一个文件</strong>。当然不仅仅路由可以按需加载，对于 <code>loadash</code> 这种大型类库同样可以使用这个功能。</p>
<p>按需加载的代码实现这里就不详细展开了，因为鉴于用的框架不同，实现起来都是不一样的。当然了，虽然他们的用法可能不同，但是底层的机制都是一样的。都是当使用的时候再去下载对应文件，返回一个 <code>Promise</code>，当 <code>Promise</code> 成功以后去执行回调。</p>
<h3 id="Scope-Hoisting"><a href="#Scope-Hoisting" class="headerlink" title="Scope Hoisting"></a>Scope Hoisting</h3><p><strong>Scope Hoisting 会分析出模块之间的依赖关系，尽可能的把打包出来的模块合并到一个函数中去。</strong></p>
<p>比如我们希望打包两个文件</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// test.js</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> a = <span class="number">1</span></span><br><span class="line"><span class="comment">// index.js</span></span><br><span class="line"><span class="keyword">import</span> &#123; a &#125; <span class="keyword">from</span> <span class="string">&#x27;./test.js&#x27;</span></span><br></pre></td></tr></table></figure>

<p>对于这种情况，我们打包出来的代码会类似这样</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">[</span><br><span class="line">  <span class="comment">/* 0 */</span></span><br><span class="line">  <span class="function"><span class="keyword">function</span> (<span class="params"><span class="built_in">module</span>, <span class="built_in">exports</span>, <span class="built_in">require</span></span>) </span>&#123;</span><br><span class="line">    <span class="comment">//...</span></span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="comment">/* 1 */</span></span><br><span class="line">  <span class="function"><span class="keyword">function</span> (<span class="params"><span class="built_in">module</span>, <span class="built_in">exports</span>, <span class="built_in">require</span></span>) </span>&#123;</span><br><span class="line">    <span class="comment">//...</span></span><br><span class="line">  &#125;</span><br><span class="line">]</span><br></pre></td></tr></table></figure>

<p>但是如果我们使用 Scope Hoisting 的话，代码就会尽可能的合并到一个函数中去，也就变成了这样的类似代码</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">[</span><br><span class="line">  <span class="comment">/* 0 */</span></span><br><span class="line">  <span class="function"><span class="keyword">function</span> (<span class="params"><span class="built_in">module</span>, <span class="built_in">exports</span>, <span class="built_in">require</span></span>) </span>&#123;</span><br><span class="line">    <span class="comment">//...</span></span><br><span class="line">  &#125;</span><br><span class="line">]</span><br></pre></td></tr></table></figure>

<p>这样的打包方式生成的代码明显比之前的少多了。如果在 Webpack4 中你希望开启这个功能，只需要启用 <code>optimization.concatenateModules</code> 就可以了。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">  optimization: &#123;</span><br><span class="line">    concatenateModules: <span class="literal">true</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="Tree-Shaking"><a href="#Tree-Shaking" class="headerlink" title="Tree Shaking"></a>Tree Shaking</h3><p><strong>Tree Shaking 可以实现删除项目中未被引用的代码</strong>，比如</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// test.js</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> a = <span class="number">1</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> b = <span class="number">2</span></span><br><span class="line"><span class="comment">// index.js</span></span><br><span class="line"><span class="keyword">import</span> &#123; a &#125; <span class="keyword">from</span> <span class="string">&#x27;./test.js&#x27;</span></span><br></pre></td></tr></table></figure>

<p>对于以上情况，<code>test</code> 文件中的变量 <code>b</code> 如果没有在项目中使用到的话，就不会被打包到文件中。</p>
<p>如果你使用 Webpack 4 的话，开启生产环境就会自动启动这个优化功能。</p>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kity@2.0.4/dist/kity.min.js"></script><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kityminder-core@1.4.50/dist/kityminder.core.min.js"></script><script defer="true" type="text/javascript" src="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.js"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.css">
      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="https://gz1234.gitee.io/2020/10/29/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E9%A2%98/%E5%89%8D%E7%AB%AF%E5%AE%89%E5%85%A8%E5%92%8C%E5%B7%A5%E7%A8%8B%E5%8C%96%E9%9D%A2%E8%AF%95%E9%A2%98/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="郭泽">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="郭泽">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | 郭泽">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2020/10/29/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E9%A2%98/%E5%89%8D%E7%AB%AF%E5%AE%89%E5%85%A8%E5%92%8C%E5%B7%A5%E7%A8%8B%E5%8C%96%E9%9D%A2%E8%AF%95%E9%A2%98/" class="post-title-link" itemprop="url">前端安全和工程化面试题</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2020-10-29 11:09:28" itemprop="dateCreated datePublished" datetime="2020-10-29T11:09:28+08:00">2020-10-29</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2023-05-29 14:07:54" itemprop="dateModified" datetime="2023-05-29T14:07:54+08:00">2023-05-29</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E9%A2%98/" itemprop="url" rel="index"><span itemprop="name">前端面试题</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h2 id="XSS"><a href="#XSS" class="headerlink" title="XSS"></a>XSS</h2><p>XSS 简单点来说，就是攻击者想尽一切办法将可以执行的代码注入到网页中。</p>
<p>XSS 可以分为多种类型，但是总体上我认为分为两类：<strong>持久型和非持久型</strong>。</p>
<p>持久型也就是攻击的代码被服务端写入进<strong>数据库</strong>中，这种攻击危害性很大，因为如果网站访问量很大的话，就会导致大量正常访问页面的用户都受到攻击。</p>
<p>举个例子，对于评论功能来说，就得防范持久型 XSS 攻击，因为我可以在评论中输入以下内容</p>
<p>这种情况如果前后端没有做好防御的话，这段评论就会被存储到数据库中，这样每个打开该页面的用户都会被攻击到。</p>
<p>非持久型相比于前者危害就小的多了，一般通过<strong>修改 URL 参数</strong>的方式加入攻击代码，诱导用户访问链接从而进行攻击。</p>
<p>举个例子，如果页面需要从 URL 中获取某些参数作为内容的话，不经过过滤就会导致攻击代码被执行</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!-- http://www.domain.com?name=&lt;script&gt;alert(1)&lt;/script&gt; --&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">div</span>&gt;</span>&#123;&#123;name&#125;&#125;<span class="tag">&lt;/<span class="name">div</span>&gt;</span>                                                  </span><br></pre></td></tr></table></figure>

<p>但是对于这种攻击方式来说，如果用户使用 Chrome 这类浏览器的话，浏览器就能自动帮助用户防御攻击。但是我们不能因此就不防御此类攻击了，因为我不能确保用户都使用了该类浏览器。</p>
<p>对于 XSS 攻击来说，通常有两种方式可以用来防御。</p>
<h3 id="转义字符"><a href="#转义字符" class="headerlink" title="转义字符"></a>转义字符</h3><p>首先，对于用户的输入应该是永远不信任的。最普遍的做法就是转义输入输出的内容，对于引号、尖括号、斜杠进行转义</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">escape</span>(<span class="params">str</span>) </span>&#123;</span><br><span class="line">  str = str.replace(<span class="regexp">/&amp;/g</span>, <span class="string">&#x27;&amp;amp;&#x27;</span>)</span><br><span class="line">  str = str.replace(<span class="regexp">/&lt;/g</span>, <span class="string">&#x27;&amp;lt;&#x27;</span>)</span><br><span class="line">  str = str.replace(<span class="regexp">/&gt;/g</span>, <span class="string">&#x27;&amp;gt;&#x27;</span>)</span><br><span class="line">  str = str.replace(<span class="regexp">/&quot;/g</span>, <span class="string">&#x27;&amp;quto;&#x27;</span>)</span><br><span class="line">  str = str.replace(<span class="regexp">/&#x27;/g</span>, <span class="string">&#x27;&amp;#39;&#x27;</span>)</span><br><span class="line">  str = str.replace(<span class="regexp">/`/g</span>, <span class="string">&#x27;&amp;#96;&#x27;</span>)</span><br><span class="line">  str = str.replace(<span class="regexp">/\//g</span>, <span class="string">&#x27;&amp;#x2F;&#x27;</span>)</span><br><span class="line">  <span class="keyword">return</span> str</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>通过转义可以将攻击代码 <code>&lt;script&gt;alert(1)&lt;/script&gt;</code> 变成</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// -&gt; &amp;lt;script&amp;gt;alert(1)&amp;lt;&amp;#x2F;script&amp;gt;</span></span><br><span class="line"><span class="built_in">escape</span>(<span class="string">&#x27;&lt;script&gt;alert(1)&lt;/script&gt;&#x27;</span>)</span><br></pre></td></tr></table></figure>

<p>但是对于显示富文本来说，显然不能通过上面的办法来转义所有字符，因为这样会把需要的格式也过滤掉。对于这种情况，通常采用白名单过滤的办法，当然也可以通过黑名单过滤，但是考虑到需要过滤的标签和标签属性实在太多，更加推荐使用白名单的方式。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> xss = <span class="built_in">require</span>(<span class="string">&#x27;xss&#x27;</span>)</span><br><span class="line"><span class="keyword">let</span> html = xss(<span class="string">&#x27;&lt;h1 id=&quot;title&quot;&gt;XSS Demo&lt;/h1&gt;&lt;script&gt;alert(&quot;xss&quot;);&lt;/script&gt;&#x27;</span>)</span><br><span class="line"><span class="comment">// -&gt; &lt;h1&gt;XSS Demo&lt;/h1&gt;&amp;lt;script&amp;gt;alert(&quot;xss&quot;);&amp;lt;/script&amp;gt;</span></span><br><span class="line"><span class="built_in">console</span>.log(html)</span><br></pre></td></tr></table></figure>

<p>以上示例使用了 <code>js-xss</code> 来实现，可以看到在输出中保留了 <code>h1</code> 标签且过滤了 <code>script</code> 标签。</p>
<h3 id="CSP"><a href="#CSP" class="headerlink" title="CSP"></a>CSP</h3><p>CSP 本质上就是建立白名单，开发者明确告诉浏览器哪些外部资源可以加载和执行。我们只需要配置规则，如何拦截是由浏览器自己实现的。我们可以通过这种方式来尽量减少 XSS 攻击。</p>
<p>通常可以通过两种方式来开启 CSP：</p>
<ol>
<li>设置 HTTP Header 中的 <code>Content-Security-Policy</code></li>
<li>设置 <code>meta</code> 标签的方式 <code>&lt;meta http-equiv=&quot;Content-Security-Policy&quot;&gt;</code></li>
</ol>
<p>这里以设置 HTTP Header 来举例</p>
<ul>
<li><p>只允许加载本站资源</p>
<figure class="highlight http"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">Content-Security-Policy</span>: default-src ‘self’</span><br></pre></td></tr></table></figure>
</li>
<li><p>只允许加载 HTTPS 协议图片</p>
<figure class="highlight http"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">Content-Security-Policy</span>: img-src https://*</span><br></pre></td></tr></table></figure>
</li>
<li><p>允许加载任何来源框架</p>
<figure class="highlight http"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">Content-Security-Policy</span>: child-src &#x27;none&#x27;</span><br></pre></td></tr></table></figure>

</li>
</ul>
<p>当然可以设置的属性远不止这些，你可以通过查阅 <a target="_blank" rel="noopener" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy">文档</a> 的方式来学习，这里就不过多赘述其他的属性了。</p>
<p>对于这种方式来说，只要开发者配置了正确的规则，那么即使网站存在漏洞，攻击者也不能执行它的攻击代码，并且 CSP 的兼容性也不错。</p>
<h2 id="CSRF"><a href="#CSRF" class="headerlink" title="CSRF"></a>CSRF</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">涉及面试题：什么是 CSRF 攻击？如何防范 CSRF 攻击？</span><br></pre></td></tr></table></figure>

<p>CSRF 中文名为跨站请求伪造。原理就是攻击者构造出一个后端请求地址，诱导用户点击或者通过某些途径自动发起请求。如果用户是在登录状态下的话，后端就以为是用户在操作，从而进行相应的逻辑。</p>
<p>举个例子，假设网站中有一个通过 <code>GET</code> 请求提交用户评论的接口，那么攻击者就可以在钓鱼网站中加入一个图片，图片的地址就是评论接口</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">img</span> <span class="attr">src</span>=<span class="string">&quot;http://www.domain.com/xxx?comment=&#x27;attack&#x27;&quot;</span>/&gt;</span></span><br></pre></td></tr></table></figure>

<p>那么你是否会想到使用 <code>POST</code> 方式提交请求是不是就没有这个问题了呢？其实并不是，使用这种方式也不是百分百安全的，攻击者同样可以诱导用户进入某个页面，在页面中通过表单提交 <code>POST</code> 请求。</p>
<h3 id="如何防御"><a href="#如何防御" class="headerlink" title="如何防御"></a>如何防御</h3><p>防范 CSRF 攻击可以遵循以下几种规则：</p>
<ol>
<li>Get 请求不对数据进行修改</li>
<li>不让第三方网站访问到用户 Cookie</li>
<li>阻止第三方网站请求接口</li>
<li>请求时附带验证信息，比如验证码或者 Token</li>
</ol>
<h4 id="SameSite"><a href="#SameSite" class="headerlink" title="SameSite"></a>SameSite</h4><p>可以对 Cookie 设置 <code>SameSite</code> 属性。该属性表示 Cookie 不随着跨域请求发送，可以很大程度减少 CSRF 的攻击，但是该属性目前并不是所有浏览器都兼容。</p>
<h4 id="验证-Referer"><a href="#验证-Referer" class="headerlink" title="验证 Referer"></a>验证 Referer</h4><p>对于需要防范 CSRF 的请求，我们可以通过验证 Referer 来判断该请求是否为第三方网站发起的。</p>
<h4 id="Token"><a href="#Token" class="headerlink" title="Token"></a>Token</h4><p>服务器下发一个随机 Token，每次发起请求时将 Token 携带上，服务器验证 Token 是否有效。</p>
<h2 id="点击劫持"><a href="#点击劫持" class="headerlink" title="点击劫持"></a>点击劫持</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">涉及面试题：什么是点击劫持？如何防范点击劫持？</span><br></pre></td></tr></table></figure>

<p>点击劫持是一种视觉欺骗的攻击手段。攻击者将需要攻击的网站通过 <code>iframe</code> 嵌套的方式嵌入自己的网页中，并将 <code>iframe</code> 设置为透明，在页面中透出一个按钮诱导用户点击。</p>
<p><img src="https://user-gold-cdn.xitu.io/2018/12/1/16768734d57c5f47?w=812&h=424&f=png&s=43633"></p>
<p>对于这种攻击方式，推荐防御的方法有两种。</p>
<h3 id="X-FRAME-OPTIONS"><a href="#X-FRAME-OPTIONS" class="headerlink" title="X-FRAME-OPTIONS"></a>X-FRAME-OPTIONS</h3><p><code>X-FRAME-OPTIONS</code> 是一个 HTTP 响应头，在现代浏览器有一个很好的支持。这个 HTTP 响应头 就是为了防御用 <code>iframe</code> 嵌套的点击劫持攻击。</p>
<p>该响应头有三个值可选，分别是</p>
<ul>
<li><code>DENY</code>，表示页面不允许通过 <code>iframe</code> 的方式展示</li>
<li><code>SAMEORIGIN</code>，表示页面可以在相同域名下通过 <code>iframe</code> 的方式展示</li>
<li><code>ALLOW-FROM</code>，表示页面可以在指定来源的 <code>iframe</code> 中展示</li>
</ul>
<h3 id="JS-防御"><a href="#JS-防御" class="headerlink" title="JS 防御"></a>JS 防御</h3><p>对于某些远古浏览器来说，并不能支持上面的这种方式，那我们只有通过 JS 的方式来防御点击劫持了。</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">style</span> <span class="attr">id</span>=<span class="string">&quot;click-jack&quot;</span>&gt;</span></span><br><span class="line">    html &#123;</span><br><span class="line">      display: none !important;</span><br><span class="line">    &#125;</span><br><span class="line">  <span class="tag">&lt;/<span class="name">style</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">script</span>&gt;</span></span><br><span class="line">    if (self == top) &#123;</span><br><span class="line"><span class="javascript">      <span class="keyword">var</span> style = <span class="built_in">document</span>.getElementById(<span class="string">&#x27;click-jack&#x27;</span>)</span></span><br><span class="line"><span class="javascript">      <span class="built_in">document</span>.body.removeChild(style)</span></span><br><span class="line"><span class="javascript">    &#125; <span class="keyword">else</span> &#123;</span></span><br><span class="line">      top.location = self.location</span><br><span class="line">    &#125;</span><br><span class="line">  <span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>以上代码的作用就是当通过 <code>iframe</code> 的方式加载页面时，攻击者的网页直接不显示所有内容了。</p>
<h2 id="中间人攻击"><a href="#中间人攻击" class="headerlink" title="中间人攻击"></a>中间人攻击</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">涉及面试题：什么是中间人攻击？如何防范中间人攻击？</span><br></pre></td></tr></table></figure>

<p>中间人攻击是攻击方同时与服务端和客户端建立起了连接，并让对方认为连接是安全的，但是实际上整个通信过程都被攻击者控制了。攻击者不仅能获得双方的通信信息，还能修改通信信息。</p>
<p>通常来说不建议使用公共的 Wi-Fi，因为很可能就会发生中间人攻击的情况。如果你在通信的过程中涉及到了某些敏感信息，就完全暴露给攻击方了。</p>
<p>当然防御中间人攻击其实并不难，只需要增加一个安全通道来传输信息。HTTPS 就可以用来防御中间人攻击，但是并不是说使用了 HTTPS 就可以高枕无忧了，因为如果你没有完全关闭 HTTP 访问的话，攻击方可以通过某些方式将 HTTPS 降级为 HTTP 从而实现中间人攻击。</p>
<p><img src="/2020/10/29/%E5%89%8D%E7%AB%AF%E9%9D%A2%E8%AF%95%E9%A2%98/%E5%89%8D%E7%AB%AF%E5%AE%89%E5%85%A8%E5%92%8C%E5%B7%A5%E7%A8%8B%E5%8C%96%E9%9D%A2%E8%AF%95%E9%A2%98/%E5%89%8D%E7%AB%AF%E6%BC%8F%E6%B4%9E.png" alt="前端漏洞"></p>
<h2 id="前端埋点方式"><a href="#前端埋点方式" class="headerlink" title="前端埋点方式"></a>前端埋点方式</h2><h3 id="基于ajax的埋点上报"><a href="#基于ajax的埋点上报" class="headerlink" title="基于ajax的埋点上报"></a>基于ajax的埋点上报</h3><p>一般而言，埋点域名并不是当前域名，因此请求会存在跨域风险，且如果ajax配置不正确可能会浏览器拦截。因此使用ajax这类请求并不是万全之策</p>
<h3 id="基于img做埋点上报"><a href="#基于img做埋点上报" class="headerlink" title="基于img做埋点上报"></a>基于img做埋点上报</h3><p>常使用img标签去做埋点上报，img标签加载并不需要挂载到页面上，基于js去new image()，设置其src之后就可以直接请求图片<br>img的加载不会阻塞html的解析，但img加载后并不渲染，它需要等待Render Tree生成完后才和Render Tree一起渲染出来<br>img兼容性好</p>
<h3 id="基于Navigator-sendBeacon的埋点上报"><a href="#基于Navigator-sendBeacon的埋点上报" class="headerlink" title="基于Navigator.sendBeacon的埋点上报"></a>基于Navigator.sendBeacon的埋点上报</h3><p>Navigator.sendBeacon是目前通用的埋点上报方案，Navigator.sendBeacon方法接受两个参数，第一个参数是目标服务器的 URL，第二个参数是所要发送的数据（可选），可以是任意类型（字符串、表单对象、二进制对象等等）;</p>
<h3 id="常见埋点行为"><a href="#常见埋点行为" class="headerlink" title="常见埋点行为"></a>常见埋点行为</h3><ul>
<li><p>绑定点击事件，当点击目标元素时，触发埋点上报。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">clickButton</span>(<span class="params">url, data</span>) </span>&#123;</span><br><span class="line">    navigator.sendBeacon(url, data)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>页面停留时间上报埋点</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/// 路由文件中，初始化一个startTime，当页面离开时通过路由守卫计算停留时间。</span></span><br><span class="line"><span class="keyword">let</span> url = <span class="string">&#x27;&#x27;</span><span class="comment">// 上报地址</span></span><br><span class="line"><span class="keyword">let</span> startTime = <span class="built_in">Date</span>.now()</span><br><span class="line"><span class="keyword">let</span> currentTime = <span class="string">&#x27;&#x27;</span></span><br><span class="line">router.beforeEach(<span class="function">(<span class="params">to, <span class="keyword">from</span>, next</span>) =&gt;</span> &#123; </span><br><span class="line">    <span class="keyword">if</span> (to) &#123;</span><br><span class="line">        currentTime = <span class="built_in">Date</span>.now()</span><br><span class="line">        stayTime = <span class="built_in">parseInt</span>(currentTime - startTime)</span><br><span class="line">        navigator.sendBeacon(url, &#123;<span class="attr">time</span>: stayTime&#125;)</span><br><span class="line">        startTime = <span class="built_in">Date</span>.now()</span><br><span class="line">    &#125;</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>
</li>
<li><p>错误监听埋点</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">app.config.errorHandler = <span class="function">(<span class="params">err</span>) =&gt;</span> &#123; </span><br><span class="line">    navigator.sendBeacon(url, &#123;<span class="attr">error</span>: error.message, <span class="attr">text</span>: <span class="string">&#x27;vue运行异常&#x27;</span> &#125;)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">window</span>.addEventListener(<span class="string">&#x27;error&#x27;</span>, <span class="function">(<span class="params">error</span>) =&gt;</span> &#123; </span><br><span class="line">    <span class="keyword">if</span> (error.message) &#123; </span><br><span class="line">        navigator.sendBeacon(url, &#123;<span class="attr">error</span>: error.message, <span class="attr">text</span>: <span class="string">&#x27;js执行异常&#x27;</span> &#125;)</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123; </span><br><span class="line">        navigator.sendBeacon(url, &#123;<span class="attr">error</span>: error.filename, <span class="attr">text</span>: <span class="string">&#x27;资源加载异常&#x27;</span> &#125;)</span><br><span class="line">    &#125; </span><br><span class="line">&#125;, <span class="literal">true</span>)</span><br><span class="line"></span><br><span class="line">axios.interceptors.response.use(</span><br><span class="line">  (response) =&gt; &#123;</span><br><span class="line">    <span class="keyword">if</span> (response.code == <span class="number">200</span>) &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="built_in">Promise</span>.resolve(response);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="built_in">Promise</span>.reject(response);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;,</span><br><span class="line">  (error) =&gt; &#123;</span><br><span class="line">    <span class="comment">// 返回错误逻辑</span></span><br><span class="line">    navigator.sendBeacon(url, &#123;<span class="attr">error</span>: error, <span class="attr">text</span>: <span class="string">&#x27;请求错误异常&#x27;</span> &#125;)</span><br><span class="line">  &#125;</span><br><span class="line">);</span><br></pre></td></tr></table></figure>
</li>
<li><p>内容可见埋点 通过交叉观察器去监听当前元素是否出现在页面</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">  <span class="comment">// 可见性发生变化后的回调 </span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">callback</span>(<span class="params">data</span>) </span>&#123; </span><br><span class="line">    navigator.sendBeacon(url, &#123; <span class="attr">target</span>: data[<span class="number">0</span>].target, <span class="attr">text</span>: <span class="string">&#x27;内容可见&#x27;</span> &#125;) </span><br><span class="line">&#125; </span><br><span class="line"><span class="comment">// 交叉观察器配置项 </span></span><br><span class="line"><span class="keyword">let</span> options = &#123;&#125;; </span><br><span class="line"><span class="comment">// 生成交叉观察器 </span></span><br><span class="line"><span class="keyword">const</span> observer = <span class="keyword">new</span> IntersectionObserver(callback); </span><br><span class="line"><span class="comment">// 获取目标节点 </span></span><br><span class="line"><span class="keyword">let</span> target = <span class="built_in">document</span>.getElementById(<span class="string">&quot;target&quot;</span>); </span><br><span class="line"><span class="comment">// 监听目标元素 </span></span><br><span class="line">observer.observe(target);</span><br></pre></td></tr></table></figure>
</li>
</ul>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kity@2.0.4/dist/kity.min.js"></script><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/kityminder-core@1.4.50/dist/kityminder.core.min.js"></script><script defer="true" type="text/javascript" src="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.js"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/hexo-simple-mindmap@0.2.0/dist/mindmap.min.css">
      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




  <nav class="pagination">
    <a class="extend prev" rel="prev" href="/page/3/"><i class="fa fa-angle-left" aria-label="上一页"></i></a><a class="page-number" href="/">1</a><span class="space">&hellip;</span><a class="page-number" href="/page/3/">3</a><span class="page-number current">4</span><a class="page-number" href="/page/5/">5</a><a class="page-number" href="/page/6/">6</a><a class="extend next" rel="next" href="/page/5/"><i class="fa fa-angle-right" aria-label="下一页"></i></a>
  </nav>

</div>
  </main>

  <footer class="footer">
    <div class="footer-inner">


<div class="copyright">
  &copy; 
  <span itemprop="copyrightYear">2023</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">郭泽</span>
</div>
  <div class="powered-by">由 <a href="https://hexo.io/" rel="noopener" target="_blank">Hexo</a> & <a href="https://theme-next.js.org/" rel="noopener" target="_blank">NexT.Gemini</a> 强力驱动
  </div>

    </div>
  </footer>

  
  <script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js" integrity="sha256-XL2inqUJaslATFnHdJOi9GfQ60on8Wx1C2H8DYiN1xY=" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/fancybox/3.5.7/jquery.fancybox.min.js" integrity="sha256-yt2kYMy0w8AbtF89WXb2P1rfjcP/HTHLT7097U8Y5b8=" crossorigin="anonymous"></script>
<script src="/js/comments.js"></script><script src="/js/utils.js"></script><script src="/js/motion.js"></script><script src="/js/next-boot.js"></script><script src="/js/bookmark.js"></script>

  
<script src="https://cdnjs.cloudflare.com/ajax/libs/hexo-generator-searchdb/1.4.0/search.js" integrity="sha256-vXZMYLEqsROAXkEw93GGIvaB2ab+QW6w3+1ahD9nXXA=" crossorigin="anonymous"></script>
<script src="/js/third-party/search/local-search.js"></script>

  <script class="next-config" data-name="pdf" type="application/json">{"object_url":{"url":"https://cdnjs.cloudflare.com/ajax/libs/pdfobject/2.2.8/pdfobject.min.js","integrity":"sha256-tu9j5pBilBQrWSDePOOajCUdz6hWsid/lBNzK4KgEPM="},"url":"/lib/pdf/web/viewer.html"}</script>
  <script src="/js/third-party/tags/pdf.js"></script>

  <script class="next-config" data-name="mermaid" type="application/json">{"enable":true,"theme":{"light":"default","dark":"dark"},"js":{"url":"https://cdnjs.cloudflare.com/ajax/libs/mermaid/9.1.3/mermaid.min.js","integrity":"sha256-TIYL00Rhw/8WaoUhYTLX9SKIEFdXxg+yMWSLVUbhiLg="}}</script>
  <script src="/js/third-party/tags/mermaid.js"></script>

  <script src="/js/third-party/fancybox.js"></script>


  





</body>
</html>
